@quantabit/level-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1340 @@
1
+ import React, { useState, useEffect, useCallback, useContext, createContext } from 'react';
2
+ import { BaseApiClient } from '@quantabit/sdk-config';
3
+
4
+ /**
5
+ * Level SDK - API 客户端
6
+ * 等级系统后端接口封装
7
+ *
8
+ * 使用 BaseApiClient 基类简化代码
9
+ */
10
+
11
+
12
+ /**
13
+ * 等级 API 客户端
14
+ */
15
+ class LevelApiClient extends BaseApiClient {
16
+ constructor(config = {}) {
17
+ super('/level', config);
18
+ }
19
+
20
+ // ============ 等级查询 ============
21
+
22
+ /**
23
+ * 获取我的等级信息
24
+ */
25
+ async getMyLevel() {
26
+ return this.get('/my');
27
+ }
28
+
29
+ /**
30
+ * 获取等级详情
31
+ * @param {number} level - 等级
32
+ */
33
+ async getLevelInfo(level) {
34
+ return this.get(`/${level}`);
35
+ }
36
+
37
+ /**
38
+ * 获取所有等级列表
39
+ */
40
+ async getAllLevels() {
41
+ return this.get('/list');
42
+ }
43
+
44
+ /**
45
+ * 获取升级进度
46
+ */
47
+ async getProgress() {
48
+ return this.get('/my/progress');
49
+ }
50
+
51
+ // ============ 等级权益 ============
52
+
53
+ /**
54
+ * 获取当前等级权益
55
+ */
56
+ async getMyBenefits() {
57
+ return this.get('/my/benefits');
58
+ }
59
+
60
+ /**
61
+ * 获取指定等级权益
62
+ * @param {number} level - 等级
63
+ */
64
+ async getLevelBenefits(level) {
65
+ return this.get(`/${level}/benefits`);
66
+ }
67
+
68
+ /**
69
+ * 领取等级奖励
70
+ * @param {number} level - 等级
71
+ */
72
+ async claimLevelReward(level) {
73
+ return this.post(`/${level}/claim`);
74
+ }
75
+
76
+ /**
77
+ * 获取待领取的等级奖励
78
+ */
79
+ async getPendingRewards() {
80
+ return this.get('/my/rewards/pending');
81
+ }
82
+
83
+ // ============ 经验值 ============
84
+
85
+ /**
86
+ * 获取经验值明细
87
+ * @param {Object} params - 查询参数
88
+ */
89
+ async getExpHistory(params = {}) {
90
+ return this.get('/my/exp/history', params);
91
+ }
92
+
93
+ /**
94
+ * 获取经验值来源
95
+ */
96
+ async getExpSources() {
97
+ return this.get('/exp/sources');
98
+ }
99
+
100
+ // ============ 等级排行 ============
101
+
102
+ /**
103
+ * 获取等级排行榜
104
+ * @param {Object} params - 查询参数
105
+ */
106
+ async getLeaderboard(params = {}) {
107
+ return this.get('/leaderboard', params);
108
+ }
109
+
110
+ /**
111
+ * 获取我的排名
112
+ */
113
+ async getMyRank() {
114
+ return this.get('/my/rank');
115
+ }
116
+
117
+ // ============ 特权对比 ============
118
+
119
+ /**
120
+ * 对比两个等级权益
121
+ * @param {number} level1 - 等级1
122
+ * @param {number} level2 - 等级2
123
+ */
124
+ async compareLevels(level1, level2) {
125
+ return this.get('/compare', {
126
+ level1,
127
+ level2
128
+ });
129
+ }
130
+
131
+ /**
132
+ * 获取升级建议
133
+ */
134
+ async getUpgradeSuggestions() {
135
+ return this.get('/my/upgrade-tips');
136
+ }
137
+ }
138
+
139
+ // 创建默认实例
140
+ const levelApi = new LevelApiClient();
141
+
142
+ /**
143
+ * Level SDK - 国际化 i18n
144
+ * 等级体系多语言支持
145
+ */
146
+
147
+ const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
148
+ const messages = {
149
+ zh: {
150
+ // 通用
151
+ loading: '加载中...',
152
+ level: '等级',
153
+ experience: '经验值',
154
+ currentLevel: '当前等级',
155
+ nextLevel: '下一等级',
156
+ maxLevel: '满级',
157
+ // 等级信息
158
+ levelUp: '升级啦!',
159
+ levelUpDesc: '恭喜您升级到 Lv.{level}',
160
+ expProgress: '经验进度',
161
+ expNeeded: '还需要 {exp} 经验升级',
162
+ expToNext: '距离下一级还需',
163
+ // 权益
164
+ privileges: '等级权益',
165
+ unlockAt: '等级 {level} 解锁',
166
+ unlocked: '已解锁',
167
+ locked: '未解锁',
168
+ // 统计
169
+ totalExp: '累计经验',
170
+ todayExp: '今日获得',
171
+ weeklyExp: '本周获得',
172
+ monthlyExp: '本月获得',
173
+ // 经验来源
174
+ expSource: '经验来源',
175
+ expFromTask: '完成任务',
176
+ expFromLogin: '每日登录',
177
+ expFromContent: '发布内容',
178
+ expFromInteraction: '互动奖励',
179
+ expFromInvite: '邀请奖励',
180
+ expFromPurchase: '消费奖励',
181
+ // 排行榜
182
+ leaderboard: '等级排行',
183
+ rank: '排名',
184
+ myRank: '我的排名',
185
+ top100: 'TOP 100',
186
+ // 成就
187
+ achievements: '成就',
188
+ achievementUnlocked: '成就解锁',
189
+ achievementProgress: '成就进度',
190
+ // 勋章
191
+ badges: '勋章',
192
+ badgeEarned: '获得勋章',
193
+ // 历史
194
+ expHistory: '经验记录',
195
+ noHistory: '暂无记录',
196
+ // 错误
197
+ errorLoad: '加载失败',
198
+ errorRetry: '点击重试'
199
+ },
200
+ ja: {
201
+ // 通用
202
+ loading: '読み込み中...',
203
+ level: 'レベル',
204
+ experience: '経験値',
205
+ currentLevel: '現在のレベル',
206
+ nextLevel: '次のレベル',
207
+ maxLevel: '最大レベル',
208
+ // 等级信息
209
+ levelUp: 'レベルアップ!',
210
+ levelUpDesc: 'おめでとうございます!Lv.{level}にレベルアップしました',
211
+ expProgress: '経験値の進捗',
212
+ expNeeded: '次のレベルまであと {exp} EXP',
213
+ expToNext: '次のレベルまで',
214
+ // 权益
215
+ privileges: 'レベル特典',
216
+ unlockAt: 'Lv.{level}でアンロック',
217
+ unlocked: 'アンロック済み',
218
+ locked: '未アンロック',
219
+ // 统计
220
+ totalExp: '累計経験値',
221
+ todayExp: '本日獲得',
222
+ weeklyExp: '今週獲得',
223
+ monthlyExp: '今月獲得',
224
+ // 经验来源
225
+ expSource: '経験値の獲得元',
226
+ expFromTask: 'タスク完了',
227
+ expFromLogin: 'デイリーログイン',
228
+ expFromContent: 'コンテンツ公開',
229
+ expFromInteraction: 'インタラクションボーナス',
230
+ expFromInvite: '招待ボーナス',
231
+ expFromPurchase: '購入ボーナス',
232
+ // 排行榜
233
+ leaderboard: 'レベルランキング',
234
+ rank: '順位',
235
+ myRank: '私の順位',
236
+ top100: 'TOP 100',
237
+ // 成就
238
+ achievements: '実績',
239
+ achievementUnlocked: '実績アンロック',
240
+ achievementProgress: '実績の進捗',
241
+ // 勋章
242
+ badges: 'バッジ',
243
+ badgeEarned: 'バッジ獲得',
244
+ // 历史
245
+ expHistory: '経験値履歴',
246
+ noHistory: '履歴がありません',
247
+ // 错误
248
+ errorLoad: '読み込みに失敗しました',
249
+ errorRetry: 'クリックして再試行'
250
+ },
251
+ ko: {
252
+ // 通用
253
+ loading: '로딩 중...',
254
+ level: '레벨',
255
+ experience: '경험치',
256
+ currentLevel: '현재 레벨',
257
+ nextLevel: '다음 레벨',
258
+ maxLevel: '만렙',
259
+ // 等级信息
260
+ levelUp: '레벨 업!',
261
+ levelUpDesc: '축하합니다! Lv.{level}(으)로 레벨 업하셨습니다',
262
+ expProgress: '경험치 진행률',
263
+ expNeeded: '레벨업까지 {exp} 경험치 필요',
264
+ expToNext: '다음 레벨까지',
265
+ // 权益
266
+ privileges: '레벨 혜택',
267
+ unlockAt: '레벨 {level}에서 잠금 해제',
268
+ unlocked: '잠금 해제됨',
269
+ locked: '잠김',
270
+ // 统计
271
+ totalExp: '누적 경험치',
272
+ todayExp: '오늘 획득',
273
+ weeklyExp: '이번 주 획득',
274
+ monthlyExp: '이번 달 획득',
275
+ // 经验来源
276
+ expSource: '경험치 획득처',
277
+ expFromTask: '미션 완료',
278
+ expFromLogin: '출석 체크',
279
+ expFromContent: '콘텐츠 등록',
280
+ expFromInteraction: '상호작용 보너스',
281
+ expFromInvite: '초대 보너스',
282
+ expFromPurchase: '구매 보너스',
283
+ // 排行榜
284
+ leaderboard: '레벨 랭킹',
285
+ rank: '순위',
286
+ myRank: '내 순위',
287
+ top100: 'TOP 100',
288
+ // 成就
289
+ achievements: '업적',
290
+ achievementUnlocked: '업적 달성',
291
+ achievementProgress: '업적 진행률',
292
+ // 勋章
293
+ badges: '배지',
294
+ badgeEarned: '배지 획득',
295
+ // 历史
296
+ expHistory: '경험치 내역',
297
+ noHistory: '내역이 없습니다',
298
+ // 错误
299
+ errorLoad: '불러오기 실패',
300
+ errorRetry: '클릭하여 다시 시도'
301
+ },
302
+ en: {
303
+ // General
304
+ loading: 'Loading...',
305
+ level: 'Level',
306
+ experience: 'Experience',
307
+ currentLevel: 'Current Level',
308
+ nextLevel: 'Next Level',
309
+ maxLevel: 'Max Level',
310
+ // Level Info
311
+ levelUp: 'Level Up!',
312
+ levelUpDesc: 'Congratulations! You reached Lv.{level}',
313
+ expProgress: 'EXP Progress',
314
+ expNeeded: '{exp} EXP needed to level up',
315
+ expToNext: 'To next level',
316
+ // Privileges
317
+ privileges: 'Privileges',
318
+ unlockAt: 'Unlock at Level {level}',
319
+ unlocked: 'Unlocked',
320
+ locked: 'Locked',
321
+ // Statistics
322
+ totalExp: 'Total EXP',
323
+ todayExp: 'Today',
324
+ weeklyExp: 'This Week',
325
+ monthlyExp: 'This Month',
326
+ // EXP Sources
327
+ expSource: 'EXP Sources',
328
+ expFromTask: 'Task Completion',
329
+ expFromLogin: 'Daily Login',
330
+ expFromContent: 'Content Creation',
331
+ expFromInteraction: 'Interaction Bonus',
332
+ expFromInvite: 'Referral Bonus',
333
+ expFromPurchase: 'Purchase Bonus',
334
+ // Leaderboard
335
+ leaderboard: 'Level Leaderboard',
336
+ rank: 'Rank',
337
+ myRank: 'My Rank',
338
+ top100: 'TOP 100',
339
+ // Achievements
340
+ achievements: 'Achievements',
341
+ achievementUnlocked: 'Achievement Unlocked',
342
+ achievementProgress: 'Progress',
343
+ // Badges
344
+ badges: 'Badges',
345
+ badgeEarned: 'Badge Earned',
346
+ // History
347
+ expHistory: 'EXP History',
348
+ noHistory: 'No records yet',
349
+ // Errors
350
+ errorLoad: 'Failed to load',
351
+ errorRetry: 'Click to retry'
352
+ }
353
+ };
354
+ let currentLanguage = 'zh';
355
+ function setLanguage(lang) {
356
+ if (SUPPORTED_LANGUAGES.includes(lang)) {
357
+ currentLanguage = lang;
358
+ if (typeof window !== 'undefined') {
359
+ localStorage.setItem('qbit_did_level_language', lang);
360
+ }
361
+ }
362
+ }
363
+ function getLanguage() {
364
+ if (typeof window !== 'undefined') {
365
+ const saved = localStorage.getItem('qbit_did_level_language');
366
+ if (saved && SUPPORTED_LANGUAGES.includes(saved)) {
367
+ currentLanguage = saved;
368
+ }
369
+ }
370
+ return currentLanguage;
371
+ }
372
+ function t(key, params) {
373
+ const msgs = messages[getLanguage()] || messages.zh;
374
+ let text = msgs[key] || key;
375
+ if (params) {
376
+ Object.keys(params).forEach(k => {
377
+ text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), params[k]);
378
+ });
379
+ }
380
+ return text;
381
+ }
382
+
383
+ /**
384
+ * Level SDK - Context Provider
385
+ * 等级体系状态管理
386
+ */
387
+
388
+ const LevelContext = /*#__PURE__*/createContext(null);
389
+
390
+ /**
391
+ * Level Provider 组件
392
+ */
393
+ function LevelProvider({
394
+ children,
395
+ apiUrl,
396
+ token,
397
+ language = 'zh',
398
+ onLevelUp,
399
+ onError
400
+ }) {
401
+ const [userLevel, setUserLevel] = useState(null);
402
+ const [levelConfigs, setLevelConfigs] = useState([]);
403
+ const [privileges, setPrivileges] = useState([]);
404
+ const [achievements, setAchievements] = useState([]);
405
+ const [badges, setBadges] = useState([]);
406
+ const [loading, setLoading] = useState(false);
407
+ const [error, setError] = useState(null);
408
+
409
+ // 初始化配置
410
+ useEffect(() => {
411
+ if (apiUrl) levelApi.setBaseUrl(apiUrl);
412
+ if (token) levelApi.setToken(token);
413
+ if (language) setLanguage(language);
414
+ }, [apiUrl, token, language]);
415
+ const handleError = useCallback(err => {
416
+ setError(err.message);
417
+ onError?.(err);
418
+ }, [onError]);
419
+
420
+ // ============ 加载数据 ============
421
+
422
+ /**
423
+ * 加载用户等级信息
424
+ */
425
+ const loadUserLevel = useCallback(async () => {
426
+ setLoading(true);
427
+ setError(null);
428
+ try {
429
+ const result = await levelApi.getMyLevel();
430
+ const prevLevel = userLevel?.level;
431
+ setUserLevel(result);
432
+ // 检查是否升级
433
+ if (prevLevel && result.level > prevLevel) {
434
+ onLevelUp?.(result);
435
+ }
436
+ return result;
437
+ } catch (err) {
438
+ handleError(err);
439
+ throw err;
440
+ } finally {
441
+ setLoading(false);
442
+ }
443
+ }, [userLevel, onLevelUp, handleError]);
444
+
445
+ /**
446
+ * 加载等级配置
447
+ */
448
+ const loadLevelConfigs = useCallback(async () => {
449
+ try {
450
+ const result = await levelApi.getLevelConfigs();
451
+ const configs = result.items || result.data || result || [];
452
+ setLevelConfigs(configs);
453
+ return configs;
454
+ } catch (err) {
455
+ handleError(err);
456
+ throw err;
457
+ }
458
+ }, [handleError]);
459
+
460
+ /**
461
+ * 加载权益列表
462
+ */
463
+ const loadPrivileges = useCallback(async () => {
464
+ try {
465
+ const result = await levelApi.getPrivileges();
466
+ const items = result.items || result.data || result || [];
467
+ setPrivileges(items);
468
+ return items;
469
+ } catch (err) {
470
+ handleError(err);
471
+ throw err;
472
+ }
473
+ }, [handleError]);
474
+
475
+ /**
476
+ * 加载成就列表
477
+ */
478
+ const loadAchievements = useCallback(async () => {
479
+ try {
480
+ const result = await levelApi.getAchievements();
481
+ const items = result.items || result.data || result || [];
482
+ setAchievements(items);
483
+ return items;
484
+ } catch (err) {
485
+ handleError(err);
486
+ throw err;
487
+ }
488
+ }, [handleError]);
489
+
490
+ /**
491
+ * 加载勋章列表
492
+ */
493
+ const loadBadges = useCallback(async () => {
494
+ try {
495
+ const result = await levelApi.getBadges();
496
+ const items = result.items || result.data || result || [];
497
+ setBadges(items);
498
+ return items;
499
+ } catch (err) {
500
+ handleError(err);
501
+ throw err;
502
+ }
503
+ }, [handleError]);
504
+
505
+ /**
506
+ * 初始化加载所有数据
507
+ */
508
+ const initialize = useCallback(async () => {
509
+ setLoading(true);
510
+ try {
511
+ await Promise.all([loadUserLevel(), loadLevelConfigs(), loadPrivileges()]);
512
+ } catch (err) {
513
+ // 错误已在各自的 load 函数中处理
514
+ } finally {
515
+ setLoading(false);
516
+ }
517
+ }, [loadUserLevel, loadLevelConfigs, loadPrivileges]);
518
+
519
+ // ============ 辅助方法 ============
520
+
521
+ /**
522
+ * 获取等级配置
523
+ */
524
+ const getLevelConfig = useCallback(level => {
525
+ return levelConfigs.find(c => c.level === level);
526
+ }, [levelConfigs]);
527
+
528
+ /**
529
+ * 检查权益是否已解锁
530
+ */
531
+ const isPrivilegeUnlocked = useCallback(privilegeId => {
532
+ if (!userLevel) return false;
533
+ const privilege = privileges.find(p => p.id === privilegeId);
534
+ return privilege && userLevel.level >= privilege.unlock_level;
535
+ }, [userLevel, privileges]);
536
+
537
+ /**
538
+ * 获取已解锁的权益
539
+ */
540
+ const getUnlockedPrivileges = useCallback(() => {
541
+ if (!userLevel) return [];
542
+ return privileges.filter(p => userLevel.level >= p.unlock_level);
543
+ }, [userLevel, privileges]);
544
+
545
+ /**
546
+ * 获取下一等级的权益
547
+ */
548
+ const getNextLevelPrivileges = useCallback(() => {
549
+ if (!userLevel) return [];
550
+ return privileges.filter(p => p.unlock_level === userLevel.level + 1);
551
+ }, [userLevel, privileges]);
552
+ const value = {
553
+ // 状态
554
+ userLevel,
555
+ levelConfigs,
556
+ privileges,
557
+ achievements,
558
+ badges,
559
+ loading,
560
+ error,
561
+ // 加载方法
562
+ loadUserLevel,
563
+ loadLevelConfigs,
564
+ loadPrivileges,
565
+ loadAchievements,
566
+ loadBadges,
567
+ initialize,
568
+ // 辅助方法
569
+ getLevelConfig,
570
+ isPrivilegeUnlocked,
571
+ getUnlockedPrivileges,
572
+ getNextLevelPrivileges,
573
+ // API 实例
574
+ api: levelApi
575
+ };
576
+ return /*#__PURE__*/React.createElement(LevelContext.Provider, {
577
+ value: value
578
+ }, children);
579
+ }
580
+
581
+ /**
582
+ * 使用 Level Context
583
+ */
584
+ function useLevel() {
585
+ const context = useContext(LevelContext);
586
+ if (!context) {
587
+ throw new Error('useLevel must be used within a LevelProvider');
588
+ }
589
+ return context;
590
+ }
591
+
592
+ /**
593
+ * Level SDK - useExpHistory Hook
594
+ * 经验历史记录 Hook
595
+ */
596
+
597
+
598
+ /**
599
+ * 经验历史 Hook
600
+ */
601
+ function useExpHistory() {
602
+ const {
603
+ api
604
+ } = useLevel();
605
+ const [history, setHistory] = useState([]);
606
+ const [loading, setLoading] = useState(false);
607
+ const [error, setError] = useState(null);
608
+ const [pagination, setPagination] = useState({
609
+ page: 1,
610
+ pageSize: 20,
611
+ total: 0,
612
+ totalPages: 0
613
+ });
614
+
615
+ /**
616
+ * 加载经验历史
617
+ */
618
+ const load = useCallback(async (params = {}) => {
619
+ setLoading(true);
620
+ setError(null);
621
+ try {
622
+ const result = await api.getExpHistory({
623
+ page: pagination.page,
624
+ page_size: pagination.pageSize,
625
+ ...params
626
+ });
627
+ const items = result.items || result.data || [];
628
+ setHistory(items);
629
+ setPagination(prev => ({
630
+ ...prev,
631
+ total: result.total || items.length,
632
+ totalPages: result.total_pages || Math.ceil((result.total || items.length) / prev.pageSize)
633
+ }));
634
+ return result;
635
+ } catch (err) {
636
+ setError(err.message);
637
+ throw err;
638
+ } finally {
639
+ setLoading(false);
640
+ }
641
+ }, [api, pagination.page, pagination.pageSize]);
642
+
643
+ /**
644
+ * 按来源筛选
645
+ */
646
+ const filterBySource = useCallback(async source => {
647
+ return load({
648
+ source,
649
+ page: 1
650
+ });
651
+ }, [load]);
652
+
653
+ /**
654
+ * 按日期范围筛选
655
+ */
656
+ const filterByDateRange = useCallback(async (startDate, endDate) => {
657
+ return load({
658
+ start_date: startDate,
659
+ end_date: endDate,
660
+ page: 1
661
+ });
662
+ }, [load]);
663
+
664
+ /**
665
+ * 设置页码
666
+ */
667
+ const setPage = useCallback(page => {
668
+ setPagination(prev => ({
669
+ ...prev,
670
+ page
671
+ }));
672
+ }, []);
673
+
674
+ /**
675
+ * 刷新
676
+ */
677
+ const refresh = useCallback(() => {
678
+ return load();
679
+ }, [load]);
680
+ return {
681
+ history,
682
+ loading,
683
+ error,
684
+ pagination,
685
+ load,
686
+ filterBySource,
687
+ filterByDateRange,
688
+ setPage,
689
+ refresh
690
+ };
691
+ }
692
+
693
+ /**
694
+ * Level SDK - useLeaderboard Hook
695
+ * 等级排行榜 Hook
696
+ */
697
+
698
+
699
+ /**
700
+ * 排行榜 Hook
701
+ */
702
+ function useLeaderboard(autoLoad = true) {
703
+ const {
704
+ api
705
+ } = useLevel();
706
+ const [leaderboard, setLeaderboard] = useState([]);
707
+ const [myRank, setMyRank] = useState(null);
708
+ const [loading, setLoading] = useState(false);
709
+ const [error, setError] = useState(null);
710
+ const [timeRange, setTimeRange] = useState('all'); // all, weekly, monthly
711
+
712
+ /**
713
+ * 加载排行榜
714
+ */
715
+ const load = useCallback(async (params = {}) => {
716
+ setLoading(true);
717
+ setError(null);
718
+ try {
719
+ const [boardResult, rankResult] = await Promise.all([api.getLeaderboard({
720
+ time_range: timeRange,
721
+ limit: 100,
722
+ ...params
723
+ }), api.getMyRank()]);
724
+ const items = boardResult.items || boardResult.data || boardResult || [];
725
+ setLeaderboard(items);
726
+ setMyRank(rankResult);
727
+ return {
728
+ leaderboard: items,
729
+ myRank: rankResult
730
+ };
731
+ } catch (err) {
732
+ setError(err.message);
733
+ throw err;
734
+ } finally {
735
+ setLoading(false);
736
+ }
737
+ }, [api, timeRange]);
738
+
739
+ /**
740
+ * 切换时间范围
741
+ */
742
+ const changeTimeRange = useCallback(range => {
743
+ setTimeRange(range);
744
+ }, []);
745
+
746
+ /**
747
+ * 刷新
748
+ */
749
+ const refresh = useCallback(() => {
750
+ return load();
751
+ }, [load]);
752
+
753
+ // 自动加载
754
+ useEffect(() => {
755
+ if (autoLoad) {
756
+ load();
757
+ }
758
+ }, [autoLoad, timeRange]);
759
+ return {
760
+ leaderboard,
761
+ myRank,
762
+ loading,
763
+ error,
764
+ timeRange,
765
+ load,
766
+ changeTimeRange,
767
+ refresh
768
+ };
769
+ }
770
+
771
+ /**
772
+ * Level SDK - LevelBadge 组件
773
+ * 等级徽章展示
774
+ */
775
+
776
+
777
+ /**
778
+ * 等级徽章组件
779
+ */
780
+ function LevelBadge({
781
+ level,
782
+ name,
783
+ icon,
784
+ color = '#6366f1',
785
+ size = 'medium',
786
+ // small, medium, large
787
+ showName = true,
788
+ animate = false,
789
+ className = ''
790
+ }) {
791
+ const sizeClasses = {
792
+ small: 'level-badge-sm',
793
+ medium: 'level-badge-md',
794
+ large: 'level-badge-lg'
795
+ };
796
+ return /*#__PURE__*/React.createElement("div", {
797
+ className: `level-badge ${sizeClasses[size]} ${animate ? 'level-badge-animate' : ''} ${className}`,
798
+ style: {
799
+ '--level-color': color
800
+ }
801
+ }, /*#__PURE__*/React.createElement("div", {
802
+ className: "level-badge-icon"
803
+ }, icon ? /*#__PURE__*/React.createElement("img", {
804
+ src: icon,
805
+ alt: `Level ${level}`
806
+ }) : /*#__PURE__*/React.createElement("span", {
807
+ className: "level-badge-number"
808
+ }, "Lv.", level)), showName && name && /*#__PURE__*/React.createElement("span", {
809
+ className: "level-badge-name"
810
+ }, name));
811
+ }
812
+
813
+ /**
814
+ * Level SDK - 类型定义
815
+ * 等级体系的核心类型
816
+ */
817
+
818
+ // 经验来源类型
819
+ const ExpSource = {
820
+ TASK: 'task',
821
+ // 完成任务
822
+ LOGIN: 'login',
823
+ // 每日登录
824
+ CONTENT: 'content',
825
+ // 发布内容
826
+ INTERACTION: 'interaction',
827
+ // 互动奖励
828
+ INVITE: 'invite',
829
+ // 邀请奖励
830
+ PURCHASE: 'purchase',
831
+ // 消费奖励
832
+ SYSTEM: 'system',
833
+ // 系统奖励
834
+ ADMIN: 'admin' // 管理员调整
835
+ };
836
+
837
+ // 权益类型
838
+ const PrivilegeType = {
839
+ FEATURE: 'feature',
840
+ // 功能解锁
841
+ DISCOUNT: 'discount',
842
+ // 折扣优惠
843
+ BONUS: 'bonus',
844
+ // 额外奖励
845
+ BADGE: 'badge',
846
+ // 专属徽章
847
+ LIMIT_INCREASE: 'limit',
848
+ // 额度提升
849
+ PRIORITY: 'priority',
850
+ // 优先权
851
+ EXCLUSIVE: 'exclusive' // 专属特权
852
+ };
853
+
854
+ // 成就类型
855
+ const AchievementType = {
856
+ LEVEL: 'level',
857
+ // 等级成就
858
+ EXP: 'exp',
859
+ // 经验成就
860
+ TASK: 'task',
861
+ // 任务成就
862
+ SOCIAL: 'social',
863
+ // 社交成就
864
+ CONTENT: 'content',
865
+ // 内容成就
866
+ TRADING: 'trading',
867
+ // 交易成就
868
+ SPECIAL: 'special' // 特殊成就
869
+ };
870
+
871
+ /**
872
+ * 创建默认等级配置
873
+ * @param {Object} overrides - 覆盖字段
874
+ * @returns {Object}
875
+ */
876
+ function createDefaultLevelConfig(overrides = {}) {
877
+ return {
878
+ id: null,
879
+ level: 1,
880
+ name: '',
881
+ icon: null,
882
+ color: '#6366f1',
883
+ min_exp: 0,
884
+ max_exp: 100,
885
+ privileges: [],
886
+ badge_url: null,
887
+ description: '',
888
+ ...overrides
889
+ };
890
+ }
891
+
892
+ /**
893
+ * 创建默认用户等级信息
894
+ * @param {Object} overrides - 覆盖字段
895
+ * @returns {Object}
896
+ */
897
+ function createDefaultUserLevel(overrides = {}) {
898
+ return {
899
+ user_did: null,
900
+ level: 1,
901
+ level_name: '',
902
+ current_exp: 0,
903
+ total_exp: 0,
904
+ exp_to_next: 100,
905
+ progress: 0,
906
+ privileges: [],
907
+ badges: [],
908
+ created_at: null,
909
+ updated_at: null,
910
+ ...overrides
911
+ };
912
+ }
913
+
914
+ /**
915
+ * 创建默认经验记录
916
+ * @param {Object} overrides - 覆盖字段
917
+ * @returns {Object}
918
+ */
919
+ function createDefaultExpRecord(overrides = {}) {
920
+ return {
921
+ id: null,
922
+ user_did: null,
923
+ amount: 0,
924
+ source: ExpSource.SYSTEM,
925
+ source_id: null,
926
+ description: '',
927
+ balance_after: 0,
928
+ created_at: null,
929
+ ...overrides
930
+ };
931
+ }
932
+
933
+ /**
934
+ * 创建默认权益
935
+ * @param {Object} overrides - 覆盖字段
936
+ * @returns {Object}
937
+ */
938
+ function createDefaultPrivilege(overrides = {}) {
939
+ return {
940
+ id: null,
941
+ name: '',
942
+ type: PrivilegeType.FEATURE,
943
+ description: '',
944
+ icon: null,
945
+ unlock_level: 1,
946
+ value: null,
947
+ is_active: true,
948
+ ...overrides
949
+ };
950
+ }
951
+
952
+ /**
953
+ * 创建默认成就
954
+ * @param {Object} overrides - 覆盖字段
955
+ * @returns {Object}
956
+ */
957
+ function createDefaultAchievement(overrides = {}) {
958
+ return {
959
+ id: null,
960
+ name: '',
961
+ type: AchievementType.LEVEL,
962
+ description: '',
963
+ icon: null,
964
+ condition: {},
965
+ reward_exp: 0,
966
+ reward_badge: null,
967
+ is_hidden: false,
968
+ ...overrides
969
+ };
970
+ }
971
+
972
+ /**
973
+ * 计算升级所需经验
974
+ * @param {number} level - 目标等级
975
+ * @param {string} formula - 公式类型 (linear, quadratic, exponential)
976
+ * @param {Object} params - 公式参数
977
+ * @returns {number}
978
+ */
979
+ function calculateExpForLevel(level, formula = 'quadratic', params = {}) {
980
+ const {
981
+ base = 100,
982
+ factor = 1.5
983
+ } = params;
984
+ switch (formula) {
985
+ case 'linear':
986
+ return base * level;
987
+ case 'quadratic':
988
+ return Math.floor(base * Math.pow(level, factor));
989
+ case 'exponential':
990
+ return Math.floor(base * Math.pow(factor, level - 1));
991
+ default:
992
+ return base * level;
993
+ }
994
+ }
995
+
996
+ /**
997
+ * 计算进度百分比
998
+ * @param {number} current - 当前经验
999
+ * @param {number} min - 当前等级最低经验
1000
+ * @param {number} max - 下一等级所需经验
1001
+ * @returns {number} 0-100 的百分比
1002
+ */
1003
+ function calculateProgress(current, min, max) {
1004
+ if (max <= min) return 100;
1005
+ const progress = (current - min) / (max - min) * 100;
1006
+ return Math.min(100, Math.max(0, progress));
1007
+ }
1008
+
1009
+ /**
1010
+ * Level SDK - LevelProgress 组件
1011
+ * 等级进度条
1012
+ */
1013
+
1014
+
1015
+ /**
1016
+ * 等级进度组件
1017
+ */
1018
+ function LevelProgress({
1019
+ currentLevel,
1020
+ currentExp,
1021
+ expToNext,
1022
+ totalExp,
1023
+ levelName,
1024
+ nextLevelName,
1025
+ color = '#6366f1',
1026
+ showStats = true,
1027
+ showLabels = true,
1028
+ size = 'medium',
1029
+ // small, medium, large
1030
+ animated = true,
1031
+ className = ''
1032
+ }) {
1033
+ const progress = calculateProgress(currentExp, 0, expToNext);
1034
+ const sizeClasses = {
1035
+ small: 'level-progress-sm',
1036
+ medium: 'level-progress-md',
1037
+ large: 'level-progress-lg'
1038
+ };
1039
+
1040
+ // 格式化数字
1041
+ const formatNumber = num => {
1042
+ if (num >= 10000) return (num / 10000).toFixed(1) + 'w';
1043
+ if (num >= 1000) return (num / 1000).toFixed(1) + 'k';
1044
+ return num?.toString() || '0';
1045
+ };
1046
+ return /*#__PURE__*/React.createElement("div", {
1047
+ className: `level-progress ${sizeClasses[size]} ${className}`,
1048
+ style: {
1049
+ '--level-color': color
1050
+ }
1051
+ }, showLabels && /*#__PURE__*/React.createElement("div", {
1052
+ className: "level-progress-header"
1053
+ }, /*#__PURE__*/React.createElement("div", {
1054
+ className: "level-progress-current"
1055
+ }, /*#__PURE__*/React.createElement("span", {
1056
+ className: "level-number"
1057
+ }, "Lv.", currentLevel), levelName && /*#__PURE__*/React.createElement("span", {
1058
+ className: "level-name"
1059
+ }, levelName)), /*#__PURE__*/React.createElement("div", {
1060
+ className: "level-progress-next"
1061
+ }, nextLevelName ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
1062
+ className: "level-name"
1063
+ }, nextLevelName), /*#__PURE__*/React.createElement("span", {
1064
+ className: "level-number"
1065
+ }, "Lv.", currentLevel + 1)) : /*#__PURE__*/React.createElement("span", {
1066
+ className: "level-max"
1067
+ }, t('maxLevel')))), /*#__PURE__*/React.createElement("div", {
1068
+ className: "level-progress-bar"
1069
+ }, /*#__PURE__*/React.createElement("div", {
1070
+ className: `level-progress-fill ${animated ? 'animated' : ''}`,
1071
+ style: {
1072
+ width: `${progress}%`
1073
+ }
1074
+ }), /*#__PURE__*/React.createElement("span", {
1075
+ className: "level-progress-text"
1076
+ }, progress.toFixed(1), "%")), showStats && /*#__PURE__*/React.createElement("div", {
1077
+ className: "level-progress-stats"
1078
+ }, /*#__PURE__*/React.createElement("div", {
1079
+ className: "level-stat"
1080
+ }, /*#__PURE__*/React.createElement("span", {
1081
+ className: "level-stat-label"
1082
+ }, t('currentLevel')), /*#__PURE__*/React.createElement("span", {
1083
+ className: "level-stat-value"
1084
+ }, formatNumber(currentExp))), /*#__PURE__*/React.createElement("div", {
1085
+ className: "level-stat"
1086
+ }, /*#__PURE__*/React.createElement("span", {
1087
+ className: "level-stat-label"
1088
+ }, t('expToNext')), /*#__PURE__*/React.createElement("span", {
1089
+ className: "level-stat-value"
1090
+ }, formatNumber(expToNext - currentExp))), /*#__PURE__*/React.createElement("div", {
1091
+ className: "level-stat"
1092
+ }, /*#__PURE__*/React.createElement("span", {
1093
+ className: "level-stat-label"
1094
+ }, t('totalExp')), /*#__PURE__*/React.createElement("span", {
1095
+ className: "level-stat-value"
1096
+ }, formatNumber(totalExp)))));
1097
+ }
1098
+
1099
+ /**
1100
+ * Level SDK - LevelCard 组件
1101
+ * 等级信息卡片
1102
+ */
1103
+
1104
+
1105
+ /**
1106
+ * 等级卡片组件
1107
+ */
1108
+ function LevelCard({
1109
+ userLevel,
1110
+ levelConfig,
1111
+ nextLevelConfig,
1112
+ showPrivileges = true,
1113
+ showProgress = true,
1114
+ onViewPrivileges,
1115
+ onViewHistory,
1116
+ className = ''
1117
+ }) {
1118
+ if (!userLevel) {
1119
+ return /*#__PURE__*/React.createElement("div", {
1120
+ className: `level-card level-card-empty ${className}`
1121
+ }, /*#__PURE__*/React.createElement("div", {
1122
+ className: "level-card-loading"
1123
+ }, /*#__PURE__*/React.createElement("div", {
1124
+ className: "level-loading-spinner"
1125
+ }), /*#__PURE__*/React.createElement("span", null, t('loading'))));
1126
+ }
1127
+ return /*#__PURE__*/React.createElement("div", {
1128
+ className: `level-card ${className}`
1129
+ }, /*#__PURE__*/React.createElement("div", {
1130
+ className: "level-card-header"
1131
+ }, /*#__PURE__*/React.createElement(LevelBadge, {
1132
+ level: userLevel.level,
1133
+ name: userLevel.level_name || levelConfig?.name,
1134
+ icon: levelConfig?.icon,
1135
+ color: levelConfig?.color,
1136
+ size: "large"
1137
+ }), /*#__PURE__*/React.createElement("div", {
1138
+ className: "level-card-title"
1139
+ }, /*#__PURE__*/React.createElement("h3", null, userLevel.level_name || `${t('level')} ${userLevel.level}`), /*#__PURE__*/React.createElement("p", null, levelConfig?.description))), showProgress && /*#__PURE__*/React.createElement("div", {
1140
+ className: "level-card-progress"
1141
+ }, /*#__PURE__*/React.createElement(LevelProgress, {
1142
+ currentLevel: userLevel.level,
1143
+ currentExp: userLevel.current_exp,
1144
+ expToNext: userLevel.exp_to_next,
1145
+ totalExp: userLevel.total_exp,
1146
+ levelName: userLevel.level_name,
1147
+ nextLevelName: nextLevelConfig?.name,
1148
+ color: levelConfig?.color,
1149
+ size: "large"
1150
+ })), showPrivileges && userLevel.privileges?.length > 0 && /*#__PURE__*/React.createElement("div", {
1151
+ className: "level-card-privileges"
1152
+ }, /*#__PURE__*/React.createElement("div", {
1153
+ className: "level-privileges-header"
1154
+ }, /*#__PURE__*/React.createElement("h4", null, t('privileges')), onViewPrivileges && /*#__PURE__*/React.createElement("button", {
1155
+ className: "level-link-btn",
1156
+ onClick: onViewPrivileges
1157
+ }, t('more'), " \u2192")), /*#__PURE__*/React.createElement("div", {
1158
+ className: "level-privileges-list"
1159
+ }, userLevel.privileges.slice(0, 4).map((privilege, index) => /*#__PURE__*/React.createElement("div", {
1160
+ key: privilege.id || index,
1161
+ className: "level-privilege-item"
1162
+ }, privilege.icon && /*#__PURE__*/React.createElement("span", {
1163
+ className: "level-privilege-icon"
1164
+ }, privilege.icon), /*#__PURE__*/React.createElement("span", {
1165
+ className: "level-privilege-name"
1166
+ }, privilege.name))))), /*#__PURE__*/React.createElement("div", {
1167
+ className: "level-card-actions"
1168
+ }, onViewHistory && /*#__PURE__*/React.createElement("button", {
1169
+ className: "level-btn level-btn-outline",
1170
+ onClick: onViewHistory
1171
+ }, t('expHistory')), onViewPrivileges && /*#__PURE__*/React.createElement("button", {
1172
+ className: "level-btn level-btn-primary",
1173
+ onClick: onViewPrivileges
1174
+ }, t('privileges'))));
1175
+ }
1176
+
1177
+ /**
1178
+ * Level SDK - Leaderboard 组件
1179
+ * 等级排行榜
1180
+ */
1181
+
1182
+
1183
+ /**
1184
+ * 排行榜组件
1185
+ */
1186
+ function Leaderboard({
1187
+ items = [],
1188
+ myRank = null,
1189
+ timeRange = 'all',
1190
+ onTimeRangeChange,
1191
+ loading = false,
1192
+ showTop = 100,
1193
+ className = ''
1194
+ }) {
1195
+ const [activeRange, setActiveRange] = useState(timeRange);
1196
+ const handleRangeChange = range => {
1197
+ setActiveRange(range);
1198
+ onTimeRangeChange?.(range);
1199
+ };
1200
+
1201
+ // 格式化排名
1202
+ const formatRank = rank => {
1203
+ if (rank === 1) return '🥇';
1204
+ if (rank === 2) return '🥈';
1205
+ if (rank === 3) return '🥉';
1206
+ return `#${rank}`;
1207
+ };
1208
+
1209
+ // 格式化数字
1210
+ const formatNumber = num => {
1211
+ if (num >= 10000) return (num / 10000).toFixed(1) + 'w';
1212
+ if (num >= 1000) return (num / 1000).toFixed(1) + 'k';
1213
+ return num?.toString() || '0';
1214
+ };
1215
+ return /*#__PURE__*/React.createElement("div", {
1216
+ className: `level-leaderboard ${className}`
1217
+ }, /*#__PURE__*/React.createElement("div", {
1218
+ className: "level-leaderboard-header"
1219
+ }, /*#__PURE__*/React.createElement("h3", null, t('leaderboard')), /*#__PURE__*/React.createElement("div", {
1220
+ className: "level-time-tabs"
1221
+ }, /*#__PURE__*/React.createElement("button", {
1222
+ className: activeRange === 'all' ? 'active' : '',
1223
+ onClick: () => handleRangeChange('all')
1224
+ }, "\u5168\u90E8"), /*#__PURE__*/React.createElement("button", {
1225
+ className: activeRange === 'monthly' ? 'active' : '',
1226
+ onClick: () => handleRangeChange('monthly')
1227
+ }, "\u672C\u6708"), /*#__PURE__*/React.createElement("button", {
1228
+ className: activeRange === 'weekly' ? 'active' : '',
1229
+ onClick: () => handleRangeChange('weekly')
1230
+ }, "\u672C\u5468"))), myRank && /*#__PURE__*/React.createElement("div", {
1231
+ className: "level-my-rank"
1232
+ }, /*#__PURE__*/React.createElement("span", {
1233
+ className: "level-my-rank-label"
1234
+ }, t('myRank')), /*#__PURE__*/React.createElement("span", {
1235
+ className: "level-my-rank-value"
1236
+ }, formatRank(myRank.rank)), /*#__PURE__*/React.createElement("span", {
1237
+ className: "level-my-rank-info"
1238
+ }, "Lv.", myRank.level, " \xB7 ", formatNumber(myRank.total_exp), " EXP")), loading ? /*#__PURE__*/React.createElement("div", {
1239
+ className: "level-leaderboard-loading"
1240
+ }, /*#__PURE__*/React.createElement("div", {
1241
+ className: "level-loading-spinner"
1242
+ }), /*#__PURE__*/React.createElement("span", null, t('loading'))) : items.length === 0 ? /*#__PURE__*/React.createElement("div", {
1243
+ className: "level-leaderboard-empty"
1244
+ }, /*#__PURE__*/React.createElement("span", null, "\u6682\u65E0\u6570\u636E")) : /*#__PURE__*/React.createElement("div", {
1245
+ className: "level-leaderboard-list"
1246
+ }, items.slice(0, showTop).map((item, index) => /*#__PURE__*/React.createElement("div", {
1247
+ key: item.user_did || index,
1248
+ className: `level-leaderboard-item ${index < 3 ? 'top-three' : ''}`
1249
+ }, /*#__PURE__*/React.createElement("span", {
1250
+ className: "level-rank"
1251
+ }, formatRank(index + 1)), /*#__PURE__*/React.createElement("div", {
1252
+ className: "level-user-info"
1253
+ }, item.avatar ? /*#__PURE__*/React.createElement("img", {
1254
+ src: item.avatar,
1255
+ alt: "",
1256
+ className: "level-user-avatar"
1257
+ }) : /*#__PURE__*/React.createElement("div", {
1258
+ className: "level-user-avatar-placeholder"
1259
+ }, item.name?.charAt(0) || '?'), /*#__PURE__*/React.createElement("span", {
1260
+ className: "level-user-name"
1261
+ }, item.name || 'Anonymous')), /*#__PURE__*/React.createElement(LevelBadge, {
1262
+ level: item.level,
1263
+ color: item.level_color,
1264
+ size: "small",
1265
+ showName: false
1266
+ }), /*#__PURE__*/React.createElement("span", {
1267
+ className: "level-exp"
1268
+ }, formatNumber(item.total_exp), " EXP")))));
1269
+ }
1270
+
1271
+ /**
1272
+ * Level SDK - PrivilegeList 组件
1273
+ * 权益列表展示
1274
+ */
1275
+
1276
+
1277
+ /**
1278
+ * 权益列表组件
1279
+ */
1280
+ function PrivilegeList({
1281
+ privileges = [],
1282
+ userLevel = 0,
1283
+ showLocked = true,
1284
+ showUnlockLevel = true,
1285
+ onPrivilegeClick,
1286
+ className = ''
1287
+ }) {
1288
+ // 分离已解锁和未解锁的权益
1289
+ const unlockedPrivileges = privileges.filter(p => userLevel >= p.unlock_level);
1290
+ const lockedPrivileges = privileges.filter(p => userLevel < p.unlock_level);
1291
+ const renderPrivilege = (privilege, isUnlocked) => /*#__PURE__*/React.createElement("div", {
1292
+ key: privilege.id,
1293
+ className: `level-privilege ${isUnlocked ? 'unlocked' : 'locked'}`,
1294
+ onClick: () => onPrivilegeClick?.(privilege)
1295
+ }, /*#__PURE__*/React.createElement("div", {
1296
+ className: "level-privilege-icon-wrapper"
1297
+ }, privilege.icon ? /*#__PURE__*/React.createElement("span", {
1298
+ className: "level-privilege-icon"
1299
+ }, privilege.icon) : /*#__PURE__*/React.createElement("span", {
1300
+ className: "level-privilege-icon-default"
1301
+ }, "\u2728"), !isUnlocked && /*#__PURE__*/React.createElement("span", {
1302
+ className: "level-privilege-lock"
1303
+ }, "\uD83D\uDD12")), /*#__PURE__*/React.createElement("div", {
1304
+ className: "level-privilege-content"
1305
+ }, /*#__PURE__*/React.createElement("h4", {
1306
+ className: "level-privilege-name"
1307
+ }, privilege.name), privilege.description && /*#__PURE__*/React.createElement("p", {
1308
+ className: "level-privilege-desc"
1309
+ }, privilege.description), showUnlockLevel && !isUnlocked && /*#__PURE__*/React.createElement("span", {
1310
+ className: "level-privilege-unlock"
1311
+ }, t('unlockAt', {
1312
+ level: privilege.unlock_level
1313
+ }))), /*#__PURE__*/React.createElement("div", {
1314
+ className: "level-privilege-status"
1315
+ }, /*#__PURE__*/React.createElement("span", {
1316
+ className: `level-status-tag ${isUnlocked ? 'tag-unlocked' : 'tag-locked'}`
1317
+ }, isUnlocked ? t('unlocked') : t('locked'))));
1318
+ return /*#__PURE__*/React.createElement("div", {
1319
+ className: `level-privilege-list ${className}`
1320
+ }, unlockedPrivileges.length > 0 && /*#__PURE__*/React.createElement("div", {
1321
+ className: "level-privilege-section"
1322
+ }, /*#__PURE__*/React.createElement("h3", {
1323
+ className: "level-privilege-section-title"
1324
+ }, t('unlocked'), " (", unlockedPrivileges.length, ")"), /*#__PURE__*/React.createElement("div", {
1325
+ className: "level-privilege-grid"
1326
+ }, unlockedPrivileges.map(p => renderPrivilege(p, true)))), showLocked && lockedPrivileges.length > 0 && /*#__PURE__*/React.createElement("div", {
1327
+ className: "level-privilege-section"
1328
+ }, /*#__PURE__*/React.createElement("h3", {
1329
+ className: "level-privilege-section-title"
1330
+ }, t('locked'), " (", lockedPrivileges.length, ")"), /*#__PURE__*/React.createElement("div", {
1331
+ className: "level-privilege-grid"
1332
+ }, lockedPrivileges.map(p => renderPrivilege(p, false)))), privileges.length === 0 && /*#__PURE__*/React.createElement("div", {
1333
+ className: "level-privilege-empty"
1334
+ }, /*#__PURE__*/React.createElement("span", {
1335
+ className: "level-empty-icon"
1336
+ }, "\uD83C\uDF81"), /*#__PURE__*/React.createElement("span", null, "\u6682\u65E0\u6743\u76CA")));
1337
+ }
1338
+
1339
+ export { AchievementType, ExpSource, Leaderboard, LevelApiClient, LevelBadge, LevelCard, LevelProgress, LevelProvider, PrivilegeList, PrivilegeType, SUPPORTED_LANGUAGES, calculateExpForLevel, calculateProgress, createDefaultAchievement, createDefaultExpRecord, createDefaultLevelConfig, createDefaultPrivilege, createDefaultUserLevel, getLanguage, levelApi, messages, setLanguage, t, useExpHistory, useLeaderboard, useLevel };
1340
+ //# sourceMappingURL=index.esm.js.map