From 5ae38cd1bed751ad561e6ca3b14253a749aa22fa Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 11:08:46 -0400 Subject: [PATCH 01/13] Add acknowledgements --- server/src/achievement/achievement.gateway.ts | 4 ++++ server/src/challenge/challenge.gateway.ts | 11 +++++++-- server/src/event/event.gateway.ts | 22 ++++++++++++----- server/src/group/group.gateway.ts | 19 +++++++++++---- .../src/organization/organization.gateway.ts | 12 ++++++---- .../src/organization/organization.service.ts | 5 ++-- server/src/user/user.gateway.ts | 24 +++++++++++++++++-- 7 files changed, 76 insertions(+), 21 deletions(-) diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index 5685d60b..da90e052 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -51,6 +51,8 @@ export class AchievementGateway { for (const ach of achs) { await this.achievementService.emitUpdateAchievementData(ach, false, user); } + + return achs.length; } /** @@ -107,5 +109,7 @@ export class AchievementGateway { false, ); } + + return achievement.id; } } diff --git a/server/src/challenge/challenge.gateway.ts b/server/src/challenge/challenge.gateway.ts index 5ff4bbc4..4d14d4d0 100644 --- a/server/src/challenge/challenge.gateway.ts +++ b/server/src/challenge/challenge.gateway.ts @@ -56,6 +56,8 @@ export class ChallengeGateway { for (const chal of challenges) { await this.challengeService.emitUpdateChallengeData(chal, false, user); } + + return challenges.length; } // Disabled for now to prevent any cheating @@ -102,7 +104,10 @@ export class ChallengeGateway { user, 'Challenge could not be completed', ); + return false; } + + return true; } @SubscribeMessage('updateChallengeData') @@ -111,7 +116,7 @@ export class ChallengeGateway { @CallingUser() user: User, @MessageBody() data: UpdateChallengeDataDto, ) { - const challenge = await this.challengeService.getChallengeById( + let challenge = await this.challengeService.getChallengeById( data.challenge.id, ); @@ -130,7 +135,7 @@ export class ChallengeGateway { await this.challengeService.emitUpdateChallengeData(challenge, true); await this.eventService.emitUpdateEventData(ev, false); } else { - const challenge = await this.challengeService.upsertChallengeFromDto( + challenge = await this.challengeService.upsertChallengeFromDto( ability, data.challenge, ); @@ -152,5 +157,7 @@ export class ChallengeGateway { await this.eventService.emitUpdateEventData(ev, false); } } + + return challenge.id; } } diff --git a/server/src/event/event.gateway.ts b/server/src/event/event.gateway.ts index 7e37c96c..bc8cabe1 100644 --- a/server/src/event/event.gateway.ts +++ b/server/src/event/event.gateway.ts @@ -58,6 +58,8 @@ export class EventGateway { for (const ev of evs) { await this.eventService.emitUpdateEventData(ev, false, user); } + + return evs.length; } @SubscribeMessage('requestFilteredEventIds') @@ -76,6 +78,8 @@ export class EventGateway { await this.eventService.emitUpdateEventData(ev, false, user); } } + + return evs.length; } @SubscribeMessage('requestRecommendedEvents') @@ -87,6 +91,8 @@ export class EventGateway { for (const ev of evs) { await this.eventService.emitUpdateEventData(ev, false, user); } + + return evs.length; } @SubscribeMessage('requestEventLeaderData') @@ -113,6 +119,8 @@ export class EventGateway { ev, user, ); + + return Math.min(data.count, 1024); } @SubscribeMessage('requestEventTrackerData') @@ -127,6 +135,8 @@ export class EventGateway { for (const tracker of trackers) { await this.eventService.emitUpdateEventTracker(tracker, user); } + + return trackers.length; } @SubscribeMessage('useEventTrackerHint') @@ -137,9 +147,10 @@ export class EventGateway { const tracker = await this.eventService.useEventTrackerHint(user); if (tracker) { await this.eventService.emitUpdateEventTracker(tracker, user); - return; + return true; } await this.clientService.emitErrorData(user, 'Failed to track used hint!'); + return false; } @SubscribeMessage('updateEventData') @@ -148,7 +159,7 @@ export class EventGateway { @CallingUser() user: User, @MessageBody() data: UpdateEventDataDto, ) { - const ev = await this.eventService.getEventById(data.event.id); + let ev = await this.eventService.getEventById(data.event.id); if (data.deleted) { if (!ev || !(await this.eventService.removeEvent(ability, ev.id))) { @@ -157,10 +168,7 @@ export class EventGateway { } await this.eventService.emitUpdateEventData(ev, true); } else { - const ev = await this.eventService.upsertEventFromDto( - ability, - data.event, - ); + ev = await this.eventService.upsertEventFromDto(ability, data.event); if (!ev) { await this.clientService.emitErrorData(user, 'Failed to upsert event!'); @@ -175,5 +183,7 @@ export class EventGateway { await this.orgService.emitUpdateOrganizationData(org, false); await this.eventService.emitUpdateEventData(ev, false); } + + return ev.id; } } diff --git a/server/src/group/group.gateway.ts b/server/src/group/group.gateway.ts index bd1848d6..2205276e 100644 --- a/server/src/group/group.gateway.ts +++ b/server/src/group/group.gateway.ts @@ -39,6 +39,7 @@ export class GroupGateway { ) { const group = await this.groupService.getGroupForUser(user); await this.groupService.emitUpdateGroupData(group, false, user); + return true; } @SubscribeMessage('joinGroup') @@ -48,6 +49,7 @@ export class GroupGateway { ) { const oldGroup = await this.groupService.joinGroup(user, data.groupId); await this.groupService.updateGroupMembers(user, oldGroup); + return true; } @SubscribeMessage('leaveGroup') @@ -57,6 +59,7 @@ export class GroupGateway { ) { const oldGroup = await this.groupService.leaveGroup(user); await this.groupService.updateGroupMembers(user, oldGroup); + return true; } /** @@ -70,9 +73,14 @@ export class GroupGateway { @CallingUser() user: User, @MessageBody() data: SetCurrentEventDto, ) { - await this.groupService.setCurrentEvent(user, data.eventId); - const group = await this.groupService.getGroupForUser(user); - await this.groupService.emitUpdateGroupData(group, false, user); + const success = await this.groupService.setCurrentEvent(user, data.eventId); + + if (success) { + const group = await this.groupService.getGroupForUser(user); + await this.groupService.emitUpdateGroupData(group, false, user); + } + + return success; } @SubscribeMessage('updateGroupData') @@ -87,7 +95,7 @@ export class GroupGateway { user, 'This group does not exist!', ); - return; + return false; } if (data.deleted) { @@ -100,6 +108,8 @@ export class GroupGateway { this.clientService.subscribe(user, group.id); await this.groupService.emitUpdateGroupData(group, false); } + + return true; } @SubscribeMessage('sendGroupInvite') @@ -109,5 +119,6 @@ export class GroupGateway { ) { const group = await this.groupService.getGroupForUser(user); await this.groupService.emitGroupInvite(group, data.targetUsername, user); + return true; } } diff --git a/server/src/organization/organization.gateway.ts b/server/src/organization/organization.gateway.ts index fc730da0..49b89c45 100644 --- a/server/src/organization/organization.gateway.ts +++ b/server/src/organization/organization.gateway.ts @@ -47,6 +47,8 @@ export class OrganizationGateway { for (const org of orgs) { await this.orgService.emitUpdateOrganizationData(org, false, user); } + + return orgs.length; } /** @@ -61,12 +63,10 @@ export class OrganizationGateway { @CallingUser() user: User, @MessageBody() data: UpdateOrganizationDataDto, ) { + let org = await this.orgService.getOrganizationById(data.organization.id); if (data.deleted) { - const org = await this.orgService.getOrganizationById( - data.organization.id, - ); - if ( + !org || !(await this.orgService.removeOrganization( ability, data.organization.id, @@ -80,7 +80,7 @@ export class OrganizationGateway { } await this.orgService.emitUpdateOrganizationData(org, true); } else { - const org = await this.orgService.upsertOrganizationFromDto( + org = await this.orgService.upsertOrganizationFromDto( ability, data.organization, ); @@ -96,5 +96,7 @@ export class OrganizationGateway { this.clientService.subscribe(user, org.id); await this.orgService.emitUpdateOrganizationData(org, false); } + + return org.id; } } diff --git a/server/src/organization/organization.service.ts b/server/src/organization/organization.service.ts index 4780d687..581438d9 100644 --- a/server/src/organization/organization.service.ts +++ b/server/src/organization/organization.service.ts @@ -153,7 +153,7 @@ export class OrganizationService { } async getOrganizationById(id: string) { - return await this.prisma.organization.findFirstOrThrow({ where: { id } }); + return await this.prisma.organization.findFirst({ where: { id } }); } async getOrganizationByCode(accessCode: string) { @@ -364,7 +364,7 @@ export class OrganizationService { where: { accessCode: code }, }); - if (!org) return; + if (!org) return false; await this.prisma.organization.update({ where: { id: org.id }, @@ -375,5 +375,6 @@ export class OrganizationService { where: { id: user.id }, data: { memberOf: { connect: { id: org.id } } }, }); + return true; } } diff --git a/server/src/user/user.gateway.ts b/server/src/user/user.gateway.ts index 00f01942..a7c8940d 100644 --- a/server/src/user/user.gateway.ts +++ b/server/src/user/user.gateway.ts @@ -86,6 +86,8 @@ export class UserGateway { await this.userService.emitUpdateUserData(us, false, false, user), ), ); + + return users.length; } @SubscribeMessage('requestUserData') @@ -103,10 +105,13 @@ export class UserGateway { user, 'Error requesting user by id', ); + return false; } } else { await this.userService.emitUpdateUserData(user, false, false, user); } + + return true; } @SubscribeMessage('updateUserData') @@ -125,6 +130,7 @@ export class UserGateway { const user = await this.userService.updateUser(ability, data.user); await this.userService.emitUpdateUserData(user, false, true); } + return true; } @SubscribeMessage('setAuthToDevice') @@ -133,6 +139,7 @@ export class UserGateway { @MessageBody() data: SetAuthToDeviceDto, ) { await this.userService.setAuthType(user, AuthType.DEVICE, data.deviceId); + return true; } @SubscribeMessage('setAuthToOAuth') @@ -145,17 +152,20 @@ export class UserGateway { this.providerToAuthType(data.provider), data.authId, ); + return true; } @SubscribeMessage('banUser') async banUser(@CallingUser() user: User, @MessageBody() data: BanUserDto) { if (user.administrator) { const user = await this.userService.byId(data.userId); - if (!!user) { + if (user) { const us = await this.userService.banUser(user, data.isBanned); await this.userService.emitUpdateUserData(us, false, false); + return true; } } + return false; } @SubscribeMessage('addManager') @@ -167,6 +177,7 @@ export class UserGateway { const org = await this.orgService.getOrganizationById(data.organizationId); if ( + !org || !(await this.orgService.addManager( ability, data.email, @@ -180,6 +191,7 @@ export class UserGateway { const manager = await this.userService.byEmail(data.email); await this.clientService.subscribe(manager, org.id); await this.orgService.emitUpdateOrganizationData(org, false); + return manager.id; } @SubscribeMessage('joinOrganization') @@ -187,12 +199,19 @@ export class UserGateway { @CallingUser() user: User, @MessageBody() data: JoinOrganizationDto, ) { - await this.orgService.joinOrganization(user, data.accessCode); + const success = await this.orgService.joinOrganization( + user, + data.accessCode, + ); + + if (!success) return false; const org = await this.orgService.getOrganizationByCode(data.accessCode); await this.orgService.emitUpdateOrganizationData(org, false); await this.userService.emitUpdateUserData(user, false, false); + + return true; } @SubscribeMessage('closeAccount') @@ -205,5 +224,6 @@ export class UserGateway { await this.userService.setAuthType(user, AuthType.NONE, user.authToken); await this.userService.deleteUser(ability, user); await this.groupService.emitUpdateGroupData(group, false); + return true; } } From ef4c201f3fa8a44a86eb4c2dd74fbc274c3d076c Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 11:57:58 -0400 Subject: [PATCH 02/13] Fix build error --- scripts/dbreset.js | 3 +++ server/src/event/event.gateway.ts | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/dbreset.js b/scripts/dbreset.js index 13c424c9..de7eeab1 100644 --- a/scripts/dbreset.js +++ b/scripts/dbreset.js @@ -18,6 +18,9 @@ async function main() { execSync( "docker compose up --build --no-attach postgres --exit-code-from server" ); + chdir("./server"); + console.log("Generating prisma client"); + execSync("npx prisma generate"); console.log("Successfully reset the database!"); } diff --git a/server/src/event/event.gateway.ts b/server/src/event/event.gateway.ts index bc8cabe1..94088b27 100644 --- a/server/src/event/event.gateway.ts +++ b/server/src/event/event.gateway.ts @@ -179,8 +179,11 @@ export class EventGateway { data.event.initialOrganizationId!, ); + if (org) { + await this.orgService.emitUpdateOrganizationData(org, false); + } + this.clientService.subscribe(user, ev.id); - await this.orgService.emitUpdateOrganizationData(org, false); await this.eventService.emitUpdateEventData(ev, false); } From 7e5f49cba6c29c63790ad2bab14ac34194464c9c Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 14:56:40 -0400 Subject: [PATCH 03/13] Step 1 towards ack generator --- admin/src/components/ServerApi.tsx | 60 --------- game/lib/api/game_client_dto.dart | 22 +-- game/lib/api/game_server_api.dart | 104 ++++++-------- package-lock.json | 27 ++++ package.json | 3 +- scripts/tsconfig.json | 10 ++ scripts/updateapi-lib/apiscanner.d.ts | 2 + scripts/updateapi-lib/apiscanner.js | 30 +++++ scripts/updateapi-lib/apiscanner.ts | 44 ++++++ scripts/updateapi-lib/dartgen.d.ts | 4 + scripts/updateapi-lib/dartgen.js | 80 ++++++----- scripts/updateapi-lib/dartgen.ts | 80 ++++++----- scripts/updateapi-lib/dtoscanner.d.ts | 2 + scripts/updateapi-lib/dtoscanner.js | 187 ++++++++++++-------------- scripts/updateapi-lib/dtoscanner.ts | 4 +- scripts/updateapi-lib/tsgen.d.ts | 3 + scripts/updateapi-lib/types.d.ts | 12 ++ scripts/updateapi-lib/types.ts | 9 +- scripts/updateapi.d.ts | 1 + 19 files changed, 380 insertions(+), 304 deletions(-) create mode 100644 scripts/tsconfig.json create mode 100644 scripts/updateapi-lib/apiscanner.d.ts create mode 100644 scripts/updateapi-lib/dartgen.d.ts create mode 100644 scripts/updateapi-lib/dtoscanner.d.ts create mode 100644 scripts/updateapi-lib/tsgen.d.ts create mode 100644 scripts/updateapi-lib/types.d.ts create mode 100644 scripts/updateapi.d.ts diff --git a/admin/src/components/ServerApi.tsx b/admin/src/components/ServerApi.tsx index 173d4602..c867817e 100644 --- a/admin/src/components/ServerApi.tsx +++ b/admin/src/components/ServerApi.tsx @@ -25,10 +25,6 @@ export class ServerApi { this.send("requestChallengeData", data); } - completedChallenge(data: dto.CompletedChallengeDto) { - this.send("completedChallenge", data); - } - updateChallengeData(data: dto.UpdateChallengeDataDto) { this.send("updateChallengeData", data); } @@ -53,38 +49,10 @@ export class ServerApi { this.send("requestEventTrackerData", data); } - useEventTrackerHint(data: dto.UseEventTrackerHintDto) { - this.send("useEventTrackerHint", data); - } - updateEventData(data: dto.UpdateEventDataDto) { this.send("updateEventData", data); } - requestGroupData(data: dto.RequestGroupDataDto) { - this.send("requestGroupData", data); - } - - joinGroup(data: dto.JoinGroupDto) { - this.send("joinGroup", data); - } - - leaveGroup(data: dto.LeaveGroupDto) { - this.send("leaveGroup", data); - } - - setCurrentEvent(data: dto.SetCurrentEventDto) { - this.send("setCurrentEvent", data); - } - - updateGroupData(data: dto.UpdateGroupDataDto) { - this.send("updateGroupData", data); - } - - sendGroupInvite(data: dto.SendGroupInviteDto) { - this.send("sendGroupInvite", data); - } - requestOrganizationData(data: dto.RequestOrganizationDataDto) { this.send("requestOrganizationData", data); } @@ -97,38 +65,10 @@ export class ServerApi { this.send("requestAllUserData", data); } - requestUserData(data: dto.RequestUserDataDto) { - this.send("requestUserData", data); - } - - updateUserData(data: dto.UpdateUserDataDto) { - this.send("updateUserData", data); - } - - setAuthToDevice(data: dto.SetAuthToDeviceDto) { - this.send("setAuthToDevice", data); - } - - setAuthToOAuth(data: dto.SetAuthToOAuthDto) { - this.send("setAuthToOAuth", data); - } - - banUser(data: dto.BanUserDto) { - this.send("banUser", data); - } - addManager(data: dto.AddManagerDto) { this.send("addManager", data); } - joinOrganization(data: dto.JoinOrganizationDto) { - this.send("joinOrganization", data); - } - - closeAccount(data: dto.CloseAccountDto) { - this.send("closeAccount", data); - } - onUpdateUserData(callback: (data: dto.UpdateUserDataDto) => void) { this.socket.removeAllListeners("updateUserData"); this.socket.on("updateUserData", (data) => callback(data)); diff --git a/game/lib/api/game_client_dto.dart b/game/lib/api/game_client_dto.dart index 059a88fc..9adc3fc8 100644 --- a/game/lib/api/game_client_dto.dart +++ b/game/lib/api/game_client_dto.dart @@ -208,7 +208,7 @@ class AchievementTrackerDto { class UpdateAchievementDataDto { Map toJson() { Map fields = {}; - fields['achievement'] = achievement.toJson(); + fields['achievement'] = achievement!.toJson(); fields['deleted'] = deleted; return fields; } @@ -283,7 +283,7 @@ class LoginDto { if (aud != null) { fields['aud'] = aud!.name; } - fields['enrollmentType'] = enrollmentType.name; + fields['enrollmentType'] = enrollmentType!.name; return fields; } @@ -517,7 +517,7 @@ class RequestChallengeDataDto { class UpdateChallengeDataDto { Map toJson() { Map fields = {}; - fields['challenge'] = challenge.toJson(); + fields['challenge'] = challenge!.toJson(); fields['deleted'] = deleted; return fields; } @@ -748,7 +748,7 @@ class UpdateLeaderDataDto { fields['eventId'] = eventId; } fields['offset'] = offset; - fields['users'] = users + fields['users'] = users! .map>((dynamic val) => val!.toJson()) .toList(); return fields; @@ -1026,7 +1026,7 @@ class EventTrackerDto { fields['isRanked'] = isRanked; fields['hintsUsed'] = hintsUsed; fields['curChallengeId'] = curChallengeId; - fields['prevChallenges'] = prevChallenges + fields['prevChallenges'] = prevChallenges! .map>((dynamic val) => val!.toJson()) .toList(); return fields; @@ -1068,7 +1068,7 @@ class EventTrackerDto { class UpdateEventTrackerDataDto { Map toJson() { Map fields = {}; - fields['tracker'] = tracker.toJson(); + fields['tracker'] = tracker!.toJson(); return fields; } @@ -1090,7 +1090,7 @@ class UpdateEventTrackerDataDto { class UpdateEventDataDto { Map toJson() { Map fields = {}; - fields['event'] = event.toJson(); + fields['event'] = event!.toJson(); fields['deleted'] = deleted; return fields; } @@ -1295,7 +1295,7 @@ class GroupDto { class UpdateGroupDataDto { Map toJson() { Map fields = {}; - fields['group'] = group.toJson(); + fields['group'] = group!.toJson(); fields['deleted'] = deleted; return fields; } @@ -1457,7 +1457,7 @@ class RequestOrganizationDataDto { class UpdateOrganizationDataDto { Map toJson() { Map fields = {}; - fields['organization'] = organization.toJson(); + fields['organization'] = organization!.toJson(); fields['deleted'] = deleted; return fields; } @@ -1524,7 +1524,7 @@ class BanUserDto { class SetAuthToOAuthDto { Map toJson() { Map fields = {}; - fields['provider'] = provider.name; + fields['provider'] = provider!.name; fields['authId'] = authId; return fields; } @@ -1789,7 +1789,7 @@ class UserDto { class UpdateUserDataDto { Map toJson() { Map fields = {}; - fields['user'] = user.toJson(); + fields['user'] = user!.toJson(); fields['deleted'] = deleted; return fields; } diff --git a/game/lib/api/game_server_api.dart b/game/lib/api/game_server_api.dart index b9c7a934..7270aa53 100644 --- a/game/lib/api/game_server_api.dart +++ b/game/lib/api/game_server_api.dart @@ -3,22 +3,23 @@ // OTHERWISE YOUR CHANGES MAY BE OVERWRITTEN! import 'dart:async'; +import 'dart:convert'; import 'package:game/api/game_client_dto.dart'; import 'package:socket_io_client/socket_io_client.dart'; class GameServerApi { final Future Function() _refreshAccess; - Socket _socket; String _refreshEv = ""; dynamic _refreshDat = ""; + dynamic _refreshResolver = (arg) {}; GameServerApi(Socket socket, Future Function() refresh) : _refreshAccess = refresh, _socket = socket { _socket.onError((data) async { if (await _refreshAccess()) { - _socket.emit(_refreshEv, _refreshDat); + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); } }); } @@ -27,101 +28,74 @@ class GameServerApi { _socket = socket; _socket.onError((data) async { if (await _refreshAccess()) { - _socket.emit(_refreshEv, _refreshDat); + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); } }); } - void _invokeWithRefresh(String ev, Map data) { + Future _invokeWithRefresh(String ev, Map data) { + Completer completer = Completer(); + + final completionFunc = (arg) { + if (completer.isCompleted) { + return; + } + + completer.complete(arg); + }; + + Future.delayed(Duration(seconds: 2)) + .then((value) => completer.complete(null)); + _refreshEv = ev; _refreshDat = data; - //print(ev); - _socket.emit(ev, data); + _refreshResolver = completionFunc; + + print(ev); + _socket.emitWithAck(ev, data, ack: completionFunc); + + return completer.future; } - void requestAchievementData(RequestAchievementDataDto dto) => + Future requestAchievementData(RequestAchievementDataDto dto) => _invokeWithRefresh("requestAchievementData", dto.toJson()); - void updateAchievementData(UpdateAchievementDataDto dto) => + Future updateAchievementData(UpdateAchievementDataDto dto) => _invokeWithRefresh("updateAchievementData", dto.toJson()); - void requestChallengeData(RequestChallengeDataDto dto) => + Future requestChallengeData(RequestChallengeDataDto dto) => _invokeWithRefresh("requestChallengeData", dto.toJson()); - void completedChallenge(CompletedChallengeDto dto) => - _invokeWithRefresh("completedChallenge", dto.toJson()); - - void updateChallengeData(UpdateChallengeDataDto dto) => + Future updateChallengeData(UpdateChallengeDataDto dto) => _invokeWithRefresh("updateChallengeData", dto.toJson()); - void requestEventData(RequestEventDataDto dto) => + Future requestEventData(RequestEventDataDto dto) => _invokeWithRefresh("requestEventData", dto.toJson()); - void requestFilteredEventIds(RequestFilteredEventsDto dto) => + Future requestFilteredEventIds(RequestFilteredEventsDto dto) => _invokeWithRefresh("requestFilteredEventIds", dto.toJson()); - void requestRecommendedEvents(RequestRecommendedEventsDto dto) => + Future requestRecommendedEvents(RequestRecommendedEventsDto dto) => _invokeWithRefresh("requestRecommendedEvents", dto.toJson()); - void requestEventLeaderData(RequestEventLeaderDataDto dto) => + Future requestEventLeaderData(RequestEventLeaderDataDto dto) => _invokeWithRefresh("requestEventLeaderData", dto.toJson()); - void requestEventTrackerData(RequestEventTrackerDataDto dto) => + Future requestEventTrackerData(RequestEventTrackerDataDto dto) => _invokeWithRefresh("requestEventTrackerData", dto.toJson()); - void useEventTrackerHint(UseEventTrackerHintDto dto) => - _invokeWithRefresh("useEventTrackerHint", dto.toJson()); - - void updateEventData(UpdateEventDataDto dto) => + Future updateEventData(UpdateEventDataDto dto) => _invokeWithRefresh("updateEventData", dto.toJson()); - void requestGroupData(RequestGroupDataDto dto) => - _invokeWithRefresh("requestGroupData", dto.toJson()); - - void joinGroup(JoinGroupDto dto) => - _invokeWithRefresh("joinGroup", dto.toJson()); - - void leaveGroup(LeaveGroupDto dto) => - _invokeWithRefresh("leaveGroup", dto.toJson()); - - void setCurrentEvent(SetCurrentEventDto dto) => - _invokeWithRefresh("setCurrentEvent", dto.toJson()); - - void updateGroupData(UpdateGroupDataDto dto) => - _invokeWithRefresh("updateGroupData", dto.toJson()); - - void sendGroupInvite(SendGroupInviteDto dto) => - _invokeWithRefresh("sendGroupInvite", dto.toJson()); - - void requestOrganizationData(RequestOrganizationDataDto dto) => + Future requestOrganizationData(RequestOrganizationDataDto dto) => _invokeWithRefresh("requestOrganizationData", dto.toJson()); - void updateOrganizationData(UpdateOrganizationDataDto dto) => + Future updateOrganizationData(UpdateOrganizationDataDto dto) => _invokeWithRefresh("updateOrganizationData", dto.toJson()); - void requestAllUserData(RequestAllUserDataDto dto) => + Future requestAllUserData(RequestAllUserDataDto dto) => _invokeWithRefresh("requestAllUserData", dto.toJson()); - void requestUserData(RequestUserDataDto dto) => - _invokeWithRefresh("requestUserData", dto.toJson()); - - void updateUserData(UpdateUserDataDto dto) => - _invokeWithRefresh("updateUserData", dto.toJson()); - - void setAuthToDevice(SetAuthToDeviceDto dto) => - _invokeWithRefresh("setAuthToDevice", dto.toJson()); - - void setAuthToOAuth(SetAuthToOAuthDto dto) => - _invokeWithRefresh("setAuthToOAuth", dto.toJson()); - - void banUser(BanUserDto dto) => _invokeWithRefresh("banUser", dto.toJson()); - - void addManager(AddManagerDto dto) => - _invokeWithRefresh("addManager", dto.toJson()); - - void joinOrganization(JoinOrganizationDto dto) => - _invokeWithRefresh("joinOrganization", dto.toJson()); - - void closeAccount(CloseAccountDto dto) => - _invokeWithRefresh("closeAccount", dto.toJson()); + Future addManager(AddManagerDto dto) async => + await _invokeWithRefresh("addManager", dto.toJson()); } diff --git a/package-lock.json b/package-lock.json index 8440d061..c1e7bbd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@types/node": "^20.12.7", "ts-morph": "^22.0.0", "typescript": "^5.4.3" }, @@ -556,6 +557,14 @@ "path-browserify": "^1.0.1" } }, + "node_modules/@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1071,6 +1080,11 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -1517,6 +1531,14 @@ "path-browserify": "^1.0.1" } }, + "@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "requires": { + "undici-types": "~5.26.4" + } + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1858,6 +1880,11 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==" }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index d58b1c1c..2d1b7eb2 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,13 @@ "setup": "node ./scripts/setup.js", "updateapi": "node ./scripts/updateapi.js", "formatall": "node ./scripts/formatall.js", - "compilescripts": "tsc scripts/updateapi.ts --target es2021 --moduleResolution nodenext --module nodenext" + "compilescripts": "tsc --project scripts" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11" }, "dependencies": { + "@types/node": "^20.12.7", "ts-morph": "^22.0.0", "typescript": "^5.4.3" } diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 00000000..cc179509 --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "types": ["node"], + "declaration": true, + "target": "es2021", + "rootDir": ".", + "moduleResolution": "nodenext", + "module": "nodenext" + } +} diff --git a/scripts/updateapi-lib/apiscanner.d.ts b/scripts/updateapi-lib/apiscanner.d.ts new file mode 100644 index 00000000..58c00a8b --- /dev/null +++ b/scripts/updateapi-lib/apiscanner.d.ts @@ -0,0 +1,2 @@ +import { ApiDefs } from "./types"; +export declare function getApiDefinitions(): ApiDefs; diff --git a/scripts/updateapi-lib/apiscanner.js b/scripts/updateapi-lib/apiscanner.js index 89016e2f..54c4afb0 100644 --- a/scripts/updateapi-lib/apiscanner.js +++ b/scripts/updateapi-lib/apiscanner.js @@ -9,6 +9,7 @@ function getApiDefinitions() { project.addSourceFileAtPath("server/src/client/client.service.ts"); const apiDefs = { serverEntrypoints: new Map(), + serverAcks: new Map(), clientEntrypoints: new Map(), }; const clientFile = project.getSourceFileOrThrow("client.service.ts"); @@ -40,6 +41,35 @@ function getApiDefinitions() { .getParameterOrThrow((param) => !!param.getDecorator("MessageBody")) .getType() .getText(); + if (!func.getReturnType().getText().startsWith("Promise")) { + console.log(`Function ${ev} does not return a promise/is not async! Skipping...`); + continue; + } + let ackType = func.getReturnType().getTypeArguments()[0]; + if (ackType.isUnion()) { + const unionTypes = ackType.getUnionTypes(); + if (unionTypes.length > 2) { + console.log(`Function ${ev} has more than 1 union type! Must be in the form | ! Skipping...`); + continue; + } + ackType = + unionTypes[0].isNull() || unionTypes[0].isUndefined() + ? unionTypes[1] + : unionTypes[0]; + } + const ackTypeName = ackType.isString() + ? "string" + : ackType.isNumber() + ? "number" + : ackType.isBoolean() + ? "boolean" + : null; + console.log(ev, ackType.getText()); + if (!ackTypeName) { + console.log(`Function ${ev} does not return one of number, boolean, or string! Skipping...`); + continue; + } + apiDefs.serverAcks.set(ev, ackTypeName); if (dto.includes(".")) { dto = dto.split(".").pop(); } diff --git a/scripts/updateapi-lib/apiscanner.ts b/scripts/updateapi-lib/apiscanner.ts index a5a9b499..99550637 100644 --- a/scripts/updateapi-lib/apiscanner.ts +++ b/scripts/updateapi-lib/apiscanner.ts @@ -10,6 +10,7 @@ export function getApiDefinitions() { const apiDefs: ApiDefs = { serverEntrypoints: new Map(), + serverAcks: new Map(), clientEntrypoints: new Map(), }; @@ -49,6 +50,49 @@ export function getApiDefinitions() { .getType() .getText(); + if (!func.getReturnType().getText().startsWith("Promise")) { + console.log( + `Function ${ev} does not return a promise/is not async! Skipping...` + ); + continue; + } + + let ackType = func.getReturnType().getTypeArguments()[0]; + + if (ackType.isUnion()) { + const unionTypes = ackType.getUnionTypes(); + if (unionTypes.length > 2) { + console.log( + `Function ${ev} has more than 1 union type! Must be in the form | ! Skipping...` + ); + continue; + } + + ackType = + unionTypes[0].isNull() || unionTypes[0].isUndefined() + ? unionTypes[1] + : unionTypes[0]; + } + + const ackTypeName = ackType.isString() + ? "string" + : ackType.isNumber() + ? "number" + : ackType.isBoolean() + ? "boolean" + : null; + + console.log(ev, ackType.getText()); + + if (!ackTypeName) { + console.log( + `Function ${ev} does not return one of number, boolean, or string! Skipping...` + ); + continue; + } + + apiDefs.serverAcks.set(ev, ackTypeName); + if (dto.includes(".")) { dto = dto.split(".").pop()!; } diff --git a/scripts/updateapi-lib/dartgen.d.ts b/scripts/updateapi-lib/dartgen.d.ts new file mode 100644 index 00000000..cc81ae42 --- /dev/null +++ b/scripts/updateapi-lib/dartgen.d.ts @@ -0,0 +1,4 @@ +import { ApiDefs, DtoDefs } from "./types"; +export declare function genDartDtoFile(dtoDefs: DtoDefs): string; +export declare function getDartClientApiFile(apiDefs: ApiDefs): string; +export declare function getDartServerApiFile(apiDefs: ApiDefs): string; diff --git a/scripts/updateapi-lib/dartgen.js b/scripts/updateapi-lib/dartgen.js index 2747ee6d..87932dfc 100644 --- a/scripts/updateapi-lib/dartgen.js +++ b/scripts/updateapi-lib/dartgen.js @@ -241,40 +241,58 @@ function getDartServerApiFile(apiDefs) { class GameServerApi { final Future Function() _refreshAccess; - Socket _socket; - - String _refreshEv = ""; - dynamic _refreshDat = ""; - - GameServerApi(Socket socket, Future Function() refresh) - : _refreshAccess = refresh, - _socket = socket { - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emit(_refreshEv, _refreshDat); - } - }); - } - - void replaceSocket(Socket socket) { - _socket = socket; - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emit(_refreshEv, _refreshDat); - } - }); - } - - void _invokeWithRefresh(String ev, Map data) { - _refreshEv = ev; - _refreshDat = data; - //print(ev); - _socket.emit(ev, data); - } + + String _refreshEv = ""; + dynamic _refreshDat = ""; + dynamic _refreshResolver = (arg) {}; + + GameServerApi(Socket socket, Future Function() refresh) + : _refreshAccess = refresh, + _socket = socket { + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } + + void replaceSocket(Socket socket) { + _socket = socket; + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } + + Future _invokeWithRefresh(String ev, Map data) { + Completer completer = Completer(); + + final completionFunc = (arg) { + if (completer.isCompleted) { + return; + } + + completer.complete(arg); + }; + + Future.delayed(Duration(seconds: 2)) + .then((value) => completer.complete(null)); + + _refreshEv = ev; + _refreshDat = data; + _refreshResolver = completionFunc; + + print(ev); + _socket.emitWithAck(ev, data, ack: completionFunc); + + return completer.future; + } `; for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + const ackType = toDartType(apiDefs.serverAcks.get(ev), "x"); dartCode += ` - void ${ev}(${dto} dto) => _invokeWithRefresh( + Future<${ackType}?> ${ev}(${dto} dto) => _invokeWithRefresh( "${ev}", dto.toJson()); `; diff --git a/scripts/updateapi-lib/dartgen.ts b/scripts/updateapi-lib/dartgen.ts index 80cc036e..71b265f8 100644 --- a/scripts/updateapi-lib/dartgen.ts +++ b/scripts/updateapi-lib/dartgen.ts @@ -250,41 +250,59 @@ export function getDartServerApiFile(apiDefs: ApiDefs) { class GameServerApi { final Future Function() _refreshAccess; - Socket _socket; - - String _refreshEv = ""; - dynamic _refreshDat = ""; - - GameServerApi(Socket socket, Future Function() refresh) - : _refreshAccess = refresh, - _socket = socket { - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emit(_refreshEv, _refreshDat); - } - }); - } - - void replaceSocket(Socket socket) { - _socket = socket; - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emit(_refreshEv, _refreshDat); - } - }); - } - - void _invokeWithRefresh(String ev, Map data) { - _refreshEv = ev; - _refreshDat = data; - //print(ev); - _socket.emit(ev, data); - } + + String _refreshEv = ""; + dynamic _refreshDat = ""; + dynamic _refreshResolver = (arg) {}; + + GameServerApi(Socket socket, Future Function() refresh) + : _refreshAccess = refresh, + _socket = socket { + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } + + void replaceSocket(Socket socket) { + _socket = socket; + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } + + Future _invokeWithRefresh(String ev, Map data) { + Completer completer = Completer(); + + final completionFunc = (arg) { + if (completer.isCompleted) { + return; + } + + completer.complete(arg); + }; + + Future.delayed(Duration(seconds: 2)) + .then((value) => completer.complete(null)); + + _refreshEv = ev; + _refreshDat = data; + _refreshResolver = completionFunc; + + print(ev); + _socket.emitWithAck(ev, data, ack: completionFunc); + + return completer.future; + } `; for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + const ackType = toDartType(apiDefs.serverAcks.get(ev)!, "x"); dartCode += ` - void ${ev}(${dto} dto) => _invokeWithRefresh( + Future<${ackType}?> ${ev}(${dto} dto) async => await _invokeWithRefresh( "${ev}", dto.toJson()); `; diff --git a/scripts/updateapi-lib/dtoscanner.d.ts b/scripts/updateapi-lib/dtoscanner.d.ts new file mode 100644 index 00000000..44da02d3 --- /dev/null +++ b/scripts/updateapi-lib/dtoscanner.d.ts @@ -0,0 +1,2 @@ +import { DtoDefs } from "./types"; +export declare function getDtoDefinitions(): DtoDefs; diff --git a/scripts/updateapi-lib/dtoscanner.js b/scripts/updateapi-lib/dtoscanner.js index fd4fbd7c..f3a9f7d2 100644 --- a/scripts/updateapi-lib/dtoscanner.js +++ b/scripts/updateapi-lib/dtoscanner.js @@ -3,106 +3,95 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getDtoDefinitions = void 0; const ts_morph_1 = require("ts-morph"); function getDtoDefinitions() { - const project = new ts_morph_1.Project({}); - project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); - console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); - console.log(); - const enumDtos = new Map(); - const baseDtos = new Map(); - for (const file of project.getSourceFiles()) { - const interfs = file.getInterfaces(); - const enums = file.getEnums(); - console.log( - `${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs` - ); - for (const enum_ of enums) { - const vals = enum_.getMembers().map((val) => val.getName()); - enumDtos.set(enum_.getName(), vals); - } - for (const interf of interfs) { - const props = interf.getProperties(); - const baseDto = new Map(); - const interfName = interf.getName(); - baseDtos.set(interfName, baseDto); - for (const prop of props) { - const propType = prop.getType(); - const propName = prop.getName(); - const isOptional = prop.hasQuestionToken(); - if (propType.isString() || propType.getArrayElementType()?.isString()) { - // str - baseDto.set(propName, [ - "string", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } else if ( - propType.isNumber() || - propType.getArrayElementType()?.isNumber() - ) { - // num - baseDto.set(propName, [ - "number", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } else if ( - propType.isBoolean() || - propType.getArrayElementType()?.isBoolean() - ) { - // num - baseDto.set(propName, [ - "boolean", - propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", - isOptional, - ]); - } else if ( - propType.isInterface() || - propType.getArrayElementType()?.isInterface() || - propType.isEnum() || - propType.getArrayElementType()?.isEnum() - ) { - let name = propType.isArray() - ? propType.getArrayElementTypeOrThrow().getText() - : propType.getText(); - const isEnum = - propType.isEnum() || propType.getArrayElementType()?.isEnum(); - if (name.includes(".")) { - name = name.split(".").pop(); - } - const fieldType = propType.isArray() - ? isEnum - ? "ENUM_DTO[]" - : "DEPENDENT_DTO[]" - : isEnum - ? "ENUM_DTO" - : "DEPENDENT_DTO"; - baseDto.set(propName, [name, fieldType, isOptional]); - } else if ( - propType.isUnion() || - propType.getArrayElementType()?.isUnion() - ) { - // enum - const enumName = - interfName.replace("Dto", "") + - propName[0].toUpperCase() + - propName.substring(1) + - "Dto"; - enumDtos.set( - enumName, - propType - .getUnionTypes() - .map((t) => t.getLiteralValue()?.toString() ?? "") - ); - baseDto.set(propName, [ - enumName, - propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", - isOptional, - ]); + const project = new ts_morph_1.Project({}); + project.addSourceFilesAtPaths("server/src/*/*.dto.ts"); + console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); + console.log(); + const enumDtos = new Map(); + const baseDtos = new Map(); + for (const file of project.getSourceFiles()) { + const interfs = file.getInterfaces(); + const enums = file.getEnums(); + console.log(`${file.getBaseName()}: ${enums.length} enums, ${interfs.length} DTOs`); + for (const enum_ of enums) { + const vals = enum_.getMembers().map((val) => val.getName()); + enumDtos.set(enum_.getName(), vals); + } + for (const interf of interfs) { + const props = interf.getProperties(); + const baseDto = new Map(); + const interfName = interf.getName(); + baseDtos.set(interfName, baseDto); + for (const prop of props) { + const propType = prop.getType(); + const propName = prop.getName(); + const isOptional = prop.hasQuestionToken(); + if (propType.isString() || propType.getArrayElementType()?.isString()) { + // str + baseDto.set(propName, [ + "string", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } + else if (propType.isNumber() || + propType.getArrayElementType()?.isNumber()) { + // num + baseDto.set(propName, [ + "number", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } + else if (propType.isBoolean() || + propType.getArrayElementType()?.isBoolean()) { + // num + baseDto.set(propName, [ + "boolean", + propType.isArray() ? "PRIMITIVE[]" : "PRIMITIVE", + isOptional, + ]); + } + else if (propType.isInterface() || + propType.getArrayElementType()?.isInterface() || + propType.isEnum() || + propType.getArrayElementType()?.isEnum()) { + let name = propType.isArray() + ? propType.getArrayElementTypeOrThrow().getText() + : propType.getText(); + const isEnum = propType.isEnum() || propType.getArrayElementType()?.isEnum(); + if (name.includes(".")) { + name = name.split(".").pop(); + } + const fieldType = propType.isArray() + ? isEnum + ? "ENUM_DTO[]" + : "DEPENDENT_DTO[]" + : isEnum + ? "ENUM_DTO" + : "DEPENDENT_DTO"; + baseDto.set(propName, [name, fieldType, isOptional]); + } + else if (propType.isUnion() || + propType.getArrayElementType()?.isUnion()) { + // enum + const enumName = interfName.replace("Dto", "") + + propName[0].toUpperCase() + + propName.substring(1) + + "Dto"; + enumDtos.set(enumName, propType + .getUnionTypes() + .map((t) => t.getLiteralValue()?.toString() ?? "")); + baseDto.set(propName, [ + enumName, + propType.isArray() ? "ENUM_DTO[]" : "ENUM_DTO", + isOptional, + ]); + } + } } - } } - } - console.log(); - return { enumDtos, baseDtos }; + console.log(); + return { enumDtos, baseDtos }; } exports.getDtoDefinitions = getDtoDefinitions; diff --git a/scripts/updateapi-lib/dtoscanner.ts b/scripts/updateapi-lib/dtoscanner.ts index 4c45c940..c4d40e60 100644 --- a/scripts/updateapi-lib/dtoscanner.ts +++ b/scripts/updateapi-lib/dtoscanner.ts @@ -9,8 +9,8 @@ export function getDtoDefinitions(): DtoDefs { console.log(`Discovered ${project.getSourceFiles().length} DTO files!`); console.log(); - const enumDtos = new Map(); - const baseDtos = new Map(); + const enumDtos = new Map(); + const baseDtos = new Map(); for (const file of project.getSourceFiles()) { const interfs = file.getInterfaces(); diff --git a/scripts/updateapi-lib/tsgen.d.ts b/scripts/updateapi-lib/tsgen.d.ts new file mode 100644 index 00000000..08550461 --- /dev/null +++ b/scripts/updateapi-lib/tsgen.d.ts @@ -0,0 +1,3 @@ +import { ApiDefs, DtoDefs } from "./types"; +export declare function genTsDtoFile(dtoDefs: DtoDefs): string; +export declare function getAdminApiFile(apiDefs: ApiDefs): string; diff --git a/scripts/updateapi-lib/types.d.ts b/scripts/updateapi-lib/types.d.ts new file mode 100644 index 00000000..67861fbd --- /dev/null +++ b/scripts/updateapi-lib/types.d.ts @@ -0,0 +1,12 @@ +export type FieldType = "ENUM_DTO" | "ENUM_DTO[]" | "DEPENDENT_DTO" | "DEPENDENT_DTO[]" | "PRIMITIVE" | "PRIMITIVE[]"; +export type EnumDto = string[]; +export type BaseDto = Map; +export type DtoDefs = { + enumDtos: Map; + baseDtos: Map; +}; +export type ApiDefs = { + clientEntrypoints: Map; + serverEntrypoints: Map; + serverAcks: Map; +}; diff --git a/scripts/updateapi-lib/types.ts b/scripts/updateapi-lib/types.ts index 5bad5178..379e787b 100644 --- a/scripts/updateapi-lib/types.ts +++ b/scripts/updateapi-lib/types.ts @@ -11,11 +11,12 @@ export type EnumDto = string[]; export type BaseDto = Map; export type DtoDefs = { - enumDtos: Map; - baseDtos: Map; + enumDtos: Map; + baseDtos: Map; }; export type ApiDefs = { - clientEntrypoints: Map; // message => dto - serverEntrypoints: Map; // message => dto + clientEntrypoints: Map; // message => dto + serverEntrypoints: Map; // message => dto + serverAcks: Map; // message => ack primitive type }; diff --git a/scripts/updateapi.d.ts b/scripts/updateapi.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/scripts/updateapi.d.ts @@ -0,0 +1 @@ +export {}; From e8a7c5f4190411fde608c13d1281a8f80aa53526 Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 15:38:54 -0400 Subject: [PATCH 04/13] Finished flutter acks --- admin/src/components/ServerApi.tsx | 60 +++++++++++++++++ game/lib/api/game_server_api.dart | 99 +++++++++++++++++++++-------- scripts/updateapi-lib/apiscanner.js | 14 +--- scripts/updateapi-lib/apiscanner.ts | 18 ++---- scripts/updateapi-lib/dartgen.js | 79 +++++++++++------------ scripts/updateapi-lib/dartgen.ts | 79 +++++++++++------------ 6 files changed, 221 insertions(+), 128 deletions(-) diff --git a/admin/src/components/ServerApi.tsx b/admin/src/components/ServerApi.tsx index c867817e..173d4602 100644 --- a/admin/src/components/ServerApi.tsx +++ b/admin/src/components/ServerApi.tsx @@ -25,6 +25,10 @@ export class ServerApi { this.send("requestChallengeData", data); } + completedChallenge(data: dto.CompletedChallengeDto) { + this.send("completedChallenge", data); + } + updateChallengeData(data: dto.UpdateChallengeDataDto) { this.send("updateChallengeData", data); } @@ -49,10 +53,38 @@ export class ServerApi { this.send("requestEventTrackerData", data); } + useEventTrackerHint(data: dto.UseEventTrackerHintDto) { + this.send("useEventTrackerHint", data); + } + updateEventData(data: dto.UpdateEventDataDto) { this.send("updateEventData", data); } + requestGroupData(data: dto.RequestGroupDataDto) { + this.send("requestGroupData", data); + } + + joinGroup(data: dto.JoinGroupDto) { + this.send("joinGroup", data); + } + + leaveGroup(data: dto.LeaveGroupDto) { + this.send("leaveGroup", data); + } + + setCurrentEvent(data: dto.SetCurrentEventDto) { + this.send("setCurrentEvent", data); + } + + updateGroupData(data: dto.UpdateGroupDataDto) { + this.send("updateGroupData", data); + } + + sendGroupInvite(data: dto.SendGroupInviteDto) { + this.send("sendGroupInvite", data); + } + requestOrganizationData(data: dto.RequestOrganizationDataDto) { this.send("requestOrganizationData", data); } @@ -65,10 +97,38 @@ export class ServerApi { this.send("requestAllUserData", data); } + requestUserData(data: dto.RequestUserDataDto) { + this.send("requestUserData", data); + } + + updateUserData(data: dto.UpdateUserDataDto) { + this.send("updateUserData", data); + } + + setAuthToDevice(data: dto.SetAuthToDeviceDto) { + this.send("setAuthToDevice", data); + } + + setAuthToOAuth(data: dto.SetAuthToOAuthDto) { + this.send("setAuthToOAuth", data); + } + + banUser(data: dto.BanUserDto) { + this.send("banUser", data); + } + addManager(data: dto.AddManagerDto) { this.send("addManager", data); } + joinOrganization(data: dto.JoinOrganizationDto) { + this.send("joinOrganization", data); + } + + closeAccount(data: dto.CloseAccountDto) { + this.send("closeAccount", data); + } + onUpdateUserData(callback: (data: dto.UpdateUserDataDto) => void) { this.socket.removeAllListeners("updateUserData"); this.socket.on("updateUserData", (data) => callback(data)); diff --git a/game/lib/api/game_server_api.dart b/game/lib/api/game_server_api.dart index 7270aa53..f274e006 100644 --- a/game/lib/api/game_server_api.dart +++ b/game/lib/api/game_server_api.dart @@ -9,6 +9,7 @@ import 'package:socket_io_client/socket_io_client.dart'; class GameServerApi { final Future Function() _refreshAccess; + Socket _socket; String _refreshEv = ""; dynamic _refreshDat = ""; @@ -57,45 +58,91 @@ class GameServerApi { return completer.future; } - Future requestAchievementData(RequestAchievementDataDto dto) => - _invokeWithRefresh("requestAchievementData", dto.toJson()); + Future requestAchievementData(RequestAchievementDataDto dto) async => + await _invokeWithRefresh("requestAchievementData", dto.toJson()); - Future updateAchievementData(UpdateAchievementDataDto dto) => - _invokeWithRefresh("updateAchievementData", dto.toJson()); + Future updateAchievementData(UpdateAchievementDataDto dto) async => + await _invokeWithRefresh("updateAchievementData", dto.toJson()); - Future requestChallengeData(RequestChallengeDataDto dto) => - _invokeWithRefresh("requestChallengeData", dto.toJson()); + Future requestChallengeData(RequestChallengeDataDto dto) async => + await _invokeWithRefresh("requestChallengeData", dto.toJson()); - Future updateChallengeData(UpdateChallengeDataDto dto) => - _invokeWithRefresh("updateChallengeData", dto.toJson()); + Future completedChallenge(CompletedChallengeDto dto) async => + await _invokeWithRefresh("completedChallenge", dto.toJson()); - Future requestEventData(RequestEventDataDto dto) => - _invokeWithRefresh("requestEventData", dto.toJson()); + Future updateChallengeData(UpdateChallengeDataDto dto) async => + await _invokeWithRefresh("updateChallengeData", dto.toJson()); - Future requestFilteredEventIds(RequestFilteredEventsDto dto) => - _invokeWithRefresh("requestFilteredEventIds", dto.toJson()); + Future requestEventData(RequestEventDataDto dto) async => + await _invokeWithRefresh("requestEventData", dto.toJson()); - Future requestRecommendedEvents(RequestRecommendedEventsDto dto) => - _invokeWithRefresh("requestRecommendedEvents", dto.toJson()); + Future requestFilteredEventIds(RequestFilteredEventsDto dto) async => + await _invokeWithRefresh("requestFilteredEventIds", dto.toJson()); - Future requestEventLeaderData(RequestEventLeaderDataDto dto) => - _invokeWithRefresh("requestEventLeaderData", dto.toJson()); + Future requestRecommendedEvents( + RequestRecommendedEventsDto dto) async => + await _invokeWithRefresh("requestRecommendedEvents", dto.toJson()); - Future requestEventTrackerData(RequestEventTrackerDataDto dto) => - _invokeWithRefresh("requestEventTrackerData", dto.toJson()); + Future requestEventLeaderData(RequestEventLeaderDataDto dto) async => + await _invokeWithRefresh("requestEventLeaderData", dto.toJson()); - Future updateEventData(UpdateEventDataDto dto) => - _invokeWithRefresh("updateEventData", dto.toJson()); + Future requestEventTrackerData(RequestEventTrackerDataDto dto) async => + await _invokeWithRefresh("requestEventTrackerData", dto.toJson()); - Future requestOrganizationData(RequestOrganizationDataDto dto) => - _invokeWithRefresh("requestOrganizationData", dto.toJson()); + Future useEventTrackerHint(UseEventTrackerHintDto dto) async => + await _invokeWithRefresh("useEventTrackerHint", dto.toJson()); - Future updateOrganizationData(UpdateOrganizationDataDto dto) => - _invokeWithRefresh("updateOrganizationData", dto.toJson()); + Future updateEventData(UpdateEventDataDto dto) async => + await _invokeWithRefresh("updateEventData", dto.toJson()); - Future requestAllUserData(RequestAllUserDataDto dto) => - _invokeWithRefresh("requestAllUserData", dto.toJson()); + Future requestGroupData(RequestGroupDataDto dto) async => + await _invokeWithRefresh("requestGroupData", dto.toJson()); + + Future joinGroup(JoinGroupDto dto) async => + await _invokeWithRefresh("joinGroup", dto.toJson()); + + Future leaveGroup(LeaveGroupDto dto) async => + await _invokeWithRefresh("leaveGroup", dto.toJson()); + + Future setCurrentEvent(SetCurrentEventDto dto) async => + await _invokeWithRefresh("setCurrentEvent", dto.toJson()); + + Future updateGroupData(UpdateGroupDataDto dto) async => + await _invokeWithRefresh("updateGroupData", dto.toJson()); + + Future sendGroupInvite(SendGroupInviteDto dto) async => + await _invokeWithRefresh("sendGroupInvite", dto.toJson()); + + Future requestOrganizationData(RequestOrganizationDataDto dto) async => + await _invokeWithRefresh("requestOrganizationData", dto.toJson()); + + Future updateOrganizationData(UpdateOrganizationDataDto dto) async => + await _invokeWithRefresh("updateOrganizationData", dto.toJson()); + + Future requestAllUserData(RequestAllUserDataDto dto) async => + await _invokeWithRefresh("requestAllUserData", dto.toJson()); + + Future requestUserData(RequestUserDataDto dto) async => + await _invokeWithRefresh("requestUserData", dto.toJson()); + + Future updateUserData(UpdateUserDataDto dto) async => + await _invokeWithRefresh("updateUserData", dto.toJson()); + + Future setAuthToDevice(SetAuthToDeviceDto dto) async => + await _invokeWithRefresh("setAuthToDevice", dto.toJson()); + + Future setAuthToOAuth(SetAuthToOAuthDto dto) async => + await _invokeWithRefresh("setAuthToOAuth", dto.toJson()); + + Future banUser(BanUserDto dto) async => + await _invokeWithRefresh("banUser", dto.toJson()); Future addManager(AddManagerDto dto) async => await _invokeWithRefresh("addManager", dto.toJson()); + + Future joinOrganization(JoinOrganizationDto dto) async => + await _invokeWithRefresh("joinOrganization", dto.toJson()); + + Future closeAccount(CloseAccountDto dto) async => + await _invokeWithRefresh("closeAccount", dto.toJson()); } diff --git a/scripts/updateapi-lib/apiscanner.js b/scripts/updateapi-lib/apiscanner.js index 54c4afb0..b86e045f 100644 --- a/scripts/updateapi-lib/apiscanner.js +++ b/scripts/updateapi-lib/apiscanner.js @@ -46,7 +46,7 @@ function getApiDefinitions() { continue; } let ackType = func.getReturnType().getTypeArguments()[0]; - if (ackType.isUnion()) { + if (ackType.isUnion() && !ackType.isBoolean() && !ackType.isNumber()) { const unionTypes = ackType.getUnionTypes(); if (unionTypes.length > 2) { console.log(`Function ${ev} has more than 1 union type! Must be in the form | ! Skipping...`); @@ -57,19 +57,11 @@ function getApiDefinitions() { ? unionTypes[1] : unionTypes[0]; } - const ackTypeName = ackType.isString() - ? "string" - : ackType.isNumber() - ? "number" - : ackType.isBoolean() - ? "boolean" - : null; - console.log(ev, ackType.getText()); - if (!ackTypeName) { + if (!(ackType.isString() || ackType.isNumber() || ackType.isBoolean())) { console.log(`Function ${ev} does not return one of number, boolean, or string! Skipping...`); continue; } - apiDefs.serverAcks.set(ev, ackTypeName); + apiDefs.serverAcks.set(ev, ackType.getText()); if (dto.includes(".")) { dto = dto.split(".").pop(); } diff --git a/scripts/updateapi-lib/apiscanner.ts b/scripts/updateapi-lib/apiscanner.ts index 99550637..75d6bdcc 100644 --- a/scripts/updateapi-lib/apiscanner.ts +++ b/scripts/updateapi-lib/apiscanner.ts @@ -59,7 +59,7 @@ export function getApiDefinitions() { let ackType = func.getReturnType().getTypeArguments()[0]; - if (ackType.isUnion()) { + if (ackType.isUnion() && !ackType.isBoolean() && !ackType.isNumber()) { const unionTypes = ackType.getUnionTypes(); if (unionTypes.length > 2) { console.log( @@ -74,24 +74,16 @@ export function getApiDefinitions() { : unionTypes[0]; } - const ackTypeName = ackType.isString() - ? "string" - : ackType.isNumber() - ? "number" - : ackType.isBoolean() - ? "boolean" - : null; - - console.log(ev, ackType.getText()); - - if (!ackTypeName) { + if ( + !(ackType.isString() || ackType.isNumber() || ackType.isBoolean()) + ) { console.log( `Function ${ev} does not return one of number, boolean, or string! Skipping...` ); continue; } - apiDefs.serverAcks.set(ev, ackTypeName); + apiDefs.serverAcks.set(ev, ackType.getText()); if (dto.includes(".")) { dto = dto.split(".").pop()!; diff --git a/scripts/updateapi-lib/dartgen.js b/scripts/updateapi-lib/dartgen.js index 87932dfc..899413b2 100644 --- a/scripts/updateapi-lib/dartgen.js +++ b/scripts/updateapi-lib/dartgen.js @@ -240,59 +240,60 @@ function getDartServerApiFile(apiDefs) { import 'package:socket_io_client/socket_io_client.dart'; class GameServerApi { - final Future Function() _refreshAccess; + final Future Function() _refreshAccess; + Socket _socket; - String _refreshEv = ""; - dynamic _refreshDat = ""; - dynamic _refreshResolver = (arg) {}; + String _refreshEv = ""; + dynamic _refreshDat = ""; + dynamic _refreshResolver = (arg) {}; - GameServerApi(Socket socket, Future Function() refresh) - : _refreshAccess = refresh, - _socket = socket { - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); - } - }); - } + GameServerApi(Socket socket, Future Function() refresh) + : _refreshAccess = refresh, + _socket = socket { + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } - void replaceSocket(Socket socket) { - _socket = socket; - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); - } - }); - } + void replaceSocket(Socket socket) { + _socket = socket; + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } - Future _invokeWithRefresh(String ev, Map data) { - Completer completer = Completer(); + Future _invokeWithRefresh(String ev, Map data) { + Completer completer = Completer(); - final completionFunc = (arg) { - if (completer.isCompleted) { - return; - } + final completionFunc = (arg) { + if (completer.isCompleted) { + return; + } - completer.complete(arg); - }; + completer.complete(arg); + }; - Future.delayed(Duration(seconds: 2)) - .then((value) => completer.complete(null)); + Future.delayed(Duration(seconds: 2)) + .then((value) => completer.complete(null)); - _refreshEv = ev; - _refreshDat = data; - _refreshResolver = completionFunc; + _refreshEv = ev; + _refreshDat = data; + _refreshResolver = completionFunc; - print(ev); - _socket.emitWithAck(ev, data, ack: completionFunc); + print(ev); + _socket.emitWithAck(ev, data, ack: completionFunc); - return completer.future; - } + return completer.future; + } `; for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { const ackType = toDartType(apiDefs.serverAcks.get(ev), "x"); dartCode += ` - Future<${ackType}?> ${ev}(${dto} dto) => _invokeWithRefresh( + Future<${ackType}?> ${ev}(${dto} dto) async => await _invokeWithRefresh( "${ev}", dto.toJson()); `; diff --git a/scripts/updateapi-lib/dartgen.ts b/scripts/updateapi-lib/dartgen.ts index 71b265f8..00bd2e4d 100644 --- a/scripts/updateapi-lib/dartgen.ts +++ b/scripts/updateapi-lib/dartgen.ts @@ -249,54 +249,55 @@ export function getDartServerApiFile(apiDefs: ApiDefs) { import 'package:socket_io_client/socket_io_client.dart'; class GameServerApi { - final Future Function() _refreshAccess; + final Future Function() _refreshAccess; + Socket _socket; - String _refreshEv = ""; - dynamic _refreshDat = ""; - dynamic _refreshResolver = (arg) {}; - - GameServerApi(Socket socket, Future Function() refresh) - : _refreshAccess = refresh, - _socket = socket { - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); - } - }); - } + String _refreshEv = ""; + dynamic _refreshDat = ""; + dynamic _refreshResolver = (arg) {}; + + GameServerApi(Socket socket, Future Function() refresh) + : _refreshAccess = refresh, + _socket = socket { + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } - void replaceSocket(Socket socket) { - _socket = socket; - _socket.onError((data) async { - if (await _refreshAccess()) { - _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); - } - }); - } + void replaceSocket(Socket socket) { + _socket = socket; + _socket.onError((data) async { + if (await _refreshAccess()) { + _socket.emitWithAck(_refreshEv, _refreshDat, ack: _refreshResolver); + } + }); + } - Future _invokeWithRefresh(String ev, Map data) { - Completer completer = Completer(); + Future _invokeWithRefresh(String ev, Map data) { + Completer completer = Completer(); - final completionFunc = (arg) { - if (completer.isCompleted) { - return; - } + final completionFunc = (arg) { + if (completer.isCompleted) { + return; + } - completer.complete(arg); - }; + completer.complete(arg); + }; - Future.delayed(Duration(seconds: 2)) - .then((value) => completer.complete(null)); + Future.delayed(Duration(seconds: 2)) + .then((value) => completer.complete(null)); - _refreshEv = ev; - _refreshDat = data; - _refreshResolver = completionFunc; + _refreshEv = ev; + _refreshDat = data; + _refreshResolver = completionFunc; - print(ev); - _socket.emitWithAck(ev, data, ack: completionFunc); + print(ev); + _socket.emitWithAck(ev, data, ack: completionFunc); - return completer.future; - } + return completer.future; + } `; for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { From 0d2efcf21e2e894731079d669decb209db3819f1 Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 22:47:02 -0400 Subject: [PATCH 05/13] Added copy modal --- admin/src/components/Events.tsx | 63 ++++++++++++++++++++++ admin/src/components/ServerApi.tsx | 84 ++++++++++++++++++----------- admin/src/components/ServerData.tsx | 74 +++++++++++++++++-------- game/lib/api/game_server_api.dart | 2 +- scripts/updateapi-lib/dartgen.js | 2 +- scripts/updateapi-lib/dartgen.ts | 2 +- scripts/updateapi-lib/tsgen.js | 5 +- scripts/updateapi-lib/tsgen.ts | 5 +- 8 files changed, 178 insertions(+), 59 deletions(-) diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index 91c41d97..ea6096c5 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -44,6 +44,7 @@ function EventCard(props: { onSelect: () => void; onEdit: () => void; onDelete: () => void; + onCopy: () => void; }) { const requiredText = props.event.requiredMembers && props.event.requiredMembers < 0 @@ -99,6 +100,9 @@ function EventCard(props: { EDIT + + COPY + @@ -201,13 +205,28 @@ function toForm(event: EventDto) { ] as EntryForm[]; } +function makeCopyForm(orgOptions: string[], initialIndex: number) { + return [ + { + name: "Org", + options: orgOptions, + value: initialIndex, + }, + ] as EntryForm[]; +} + export function Events() { const serverData = useContext(ServerDataContext); const [isCreateModalOpen, setCreateModalOpen] = useState(false); const [isEditModalOpen, setEditModalOpen] = useState(false); const [isDeleteModalOpen, setDeleteModalOpen] = useState(false); const [selectModalOpen, setSelectModalOpen] = useState(false); + const [isCopyModalOpen, setCopyModalOpen] = useState(false); const [form, setForm] = useState(() => makeForm()); + const [copyForm, setCopyForm] = useState(() => ({ + form: makeCopyForm([], 0), + orgIds: [] as string[], + })); const [currentId, setCurrentId] = useState(""); const [query, setQuery] = useState(""); const selectedOrg = serverData.organizations.get(serverData.selectedOrg); @@ -252,6 +271,35 @@ export function Events() { }} form={form} /> + { + const ev = serverData.events.get(currentId)!; + const evId = await serverData.updateEvent({ + ...ev, + id: "", + }); + if (!evId) { + setCopyModalOpen(false); + return; + } + for (const chalId of ev.challenges!) { + const chal = serverData.challenges.get(chalId)!; + serverData.updateChallenge({ + ...chal, + linkedEventId: evId, + id: "", + }); + } + setCopyModalOpen(false); + }} + onCancel={() => { + setCopyModalOpen(false); + }} + form={form} + /> { + const orgs = Array.from(serverData.organizations.values()); + const myOrgIndex = orgs.findIndex( + (v) => v.id === selectedOrg?.id + ); + setCurrentId(ev.id); + setCopyForm({ + form: makeCopyForm( + orgs.map((org) => org.name ?? ""), + myOrgIndex + ), + orgIds: orgs.map((org) => org.id), + }); + setCopyModalOpen(true); + }} /> ))} diff --git a/admin/src/components/ServerApi.tsx b/admin/src/components/ServerApi.tsx index 173d4602..cde09171 100644 --- a/admin/src/components/ServerApi.tsx +++ b/admin/src/components/ServerApi.tsx @@ -10,123 +10,147 @@ export class ServerApi { send(ev: string, data: {}) { console.log(`Sending ${ev} with ${JSON.stringify(data)}`); - this.socket.emit(ev, data); + return this.socket.timeout(5000).emitWithAck(ev, data); } requestAchievementData(data: dto.RequestAchievementDataDto) { - this.send("requestAchievementData", data); + return this.send("requestAchievementData", data) as Promise< + number | undefined + >; } updateAchievementData(data: dto.UpdateAchievementDataDto) { - this.send("updateAchievementData", data); + return this.send("updateAchievementData", data) as Promise< + string | undefined + >; } requestChallengeData(data: dto.RequestChallengeDataDto) { - this.send("requestChallengeData", data); + return this.send("requestChallengeData", data) as Promise< + number | undefined + >; } completedChallenge(data: dto.CompletedChallengeDto) { - this.send("completedChallenge", data); + return this.send("completedChallenge", data) as Promise< + boolean | undefined + >; } updateChallengeData(data: dto.UpdateChallengeDataDto) { - this.send("updateChallengeData", data); + return this.send("updateChallengeData", data) as Promise< + string | undefined + >; } requestEventData(data: dto.RequestEventDataDto) { - this.send("requestEventData", data); + return this.send("requestEventData", data) as Promise; } requestFilteredEventIds(data: dto.RequestFilteredEventsDto) { - this.send("requestFilteredEventIds", data); + return this.send("requestFilteredEventIds", data) as Promise< + number | undefined + >; } requestRecommendedEvents(data: dto.RequestRecommendedEventsDto) { - this.send("requestRecommendedEvents", data); + return this.send("requestRecommendedEvents", data) as Promise< + number | undefined + >; } requestEventLeaderData(data: dto.RequestEventLeaderDataDto) { - this.send("requestEventLeaderData", data); + return this.send("requestEventLeaderData", data) as Promise< + number | undefined + >; } requestEventTrackerData(data: dto.RequestEventTrackerDataDto) { - this.send("requestEventTrackerData", data); + return this.send("requestEventTrackerData", data) as Promise< + number | undefined + >; } useEventTrackerHint(data: dto.UseEventTrackerHintDto) { - this.send("useEventTrackerHint", data); + return this.send("useEventTrackerHint", data) as Promise< + boolean | undefined + >; } updateEventData(data: dto.UpdateEventDataDto) { - this.send("updateEventData", data); + return this.send("updateEventData", data) as Promise; } requestGroupData(data: dto.RequestGroupDataDto) { - this.send("requestGroupData", data); + return this.send("requestGroupData", data) as Promise; } joinGroup(data: dto.JoinGroupDto) { - this.send("joinGroup", data); + return this.send("joinGroup", data) as Promise; } leaveGroup(data: dto.LeaveGroupDto) { - this.send("leaveGroup", data); + return this.send("leaveGroup", data) as Promise; } setCurrentEvent(data: dto.SetCurrentEventDto) { - this.send("setCurrentEvent", data); + return this.send("setCurrentEvent", data) as Promise; } updateGroupData(data: dto.UpdateGroupDataDto) { - this.send("updateGroupData", data); + return this.send("updateGroupData", data) as Promise; } sendGroupInvite(data: dto.SendGroupInviteDto) { - this.send("sendGroupInvite", data); + return this.send("sendGroupInvite", data) as Promise; } requestOrganizationData(data: dto.RequestOrganizationDataDto) { - this.send("requestOrganizationData", data); + return this.send("requestOrganizationData", data) as Promise< + number | undefined + >; } updateOrganizationData(data: dto.UpdateOrganizationDataDto) { - this.send("updateOrganizationData", data); + return this.send("updateOrganizationData", data) as Promise< + string | undefined + >; } requestAllUserData(data: dto.RequestAllUserDataDto) { - this.send("requestAllUserData", data); + return this.send("requestAllUserData", data) as Promise; } requestUserData(data: dto.RequestUserDataDto) { - this.send("requestUserData", data); + return this.send("requestUserData", data) as Promise; } updateUserData(data: dto.UpdateUserDataDto) { - this.send("updateUserData", data); + return this.send("updateUserData", data) as Promise; } setAuthToDevice(data: dto.SetAuthToDeviceDto) { - this.send("setAuthToDevice", data); + return this.send("setAuthToDevice", data) as Promise; } setAuthToOAuth(data: dto.SetAuthToOAuthDto) { - this.send("setAuthToOAuth", data); + return this.send("setAuthToOAuth", data) as Promise; } banUser(data: dto.BanUserDto) { - this.send("banUser", data); + return this.send("banUser", data) as Promise; } addManager(data: dto.AddManagerDto) { - this.send("addManager", data); + return this.send("addManager", data) as Promise; } joinOrganization(data: dto.JoinOrganizationDto) { - this.send("joinOrganization", data); + return this.send("joinOrganization", data) as Promise; } closeAccount(data: dto.CloseAccountDto) { - this.send("closeAccount", data); + return this.send("closeAccount", data) as Promise; } onUpdateUserData(callback: (data: dto.UpdateUserDataDto) => void) { diff --git a/admin/src/components/ServerData.tsx b/admin/src/components/ServerData.tsx index e17a123c..b07bb34f 100644 --- a/admin/src/components/ServerData.tsx +++ b/admin/src/components/ServerData.tsx @@ -31,18 +31,45 @@ const defaultData = { selectEvent(id: string) {}, selectOrg(id: string) {}, setAdminStatus(id: string, granted: boolean) {}, - updateChallenge(challenge: ChallengeDto) {}, - deleteChallenge(id: string) {}, - updateEvent(event: EventDto) {}, - deleteEvent(id: string) {}, - updateOrganization(organization: OrganizationDto) {}, - addManager(email: string, orgniazationId: string) {}, - deleteOrganization(id: string) {}, + async updateChallenge(challenge: ChallengeDto): Promise { + return undefined; + }, + async deleteChallenge(id: string): Promise { + return undefined; + }, + async updateEvent(event: EventDto): Promise { + return undefined; + }, + async deleteEvent(id: string): Promise { + return undefined; + }, + async updateOrganization( + organization: OrganizationDto + ): Promise { + return undefined; + }, + async addManager( + email: string, + orgniazationId: string + ): Promise { + return undefined; + }, + async deleteOrganization(id: string): Promise { + return undefined; + }, deleteError(id: string) {}, - updateUser(user: UserDto) {}, - deleteUser(id: string) {}, - updateGroup(event: GroupDto) {}, - deleteGroup(id: string) {}, + async updateUser(user: UserDto): Promise { + return undefined; + }, + async deleteUser(id: string): Promise { + return undefined; + }, + async updateGroup(event: GroupDto): Promise { + return undefined; + }, + async deleteGroup(id: string): Promise { + return undefined; + }, }; export const ServerDataContext = createContext(defaultData); @@ -78,44 +105,47 @@ export function ServerDataProvider(props: { children: ReactNode }) { }); }, updateChallenge(challenge: ChallengeDto) { - sock.updateChallengeData({ challenge, deleted: false }); + return sock.updateChallengeData({ challenge, deleted: false }); }, deleteChallenge(id: string) { - sock.updateChallengeData({ challenge: { id }, deleted: true }); + return sock.updateChallengeData({ challenge: { id }, deleted: true }); }, updateEvent(event: EventDto) { - sock.updateEventData({ event: event, deleted: false }); + return sock.updateEventData({ event: event, deleted: false }); }, deleteEvent(id: string) { - sock.updateEventData({ event: { id }, deleted: true }); + return sock.updateEventData({ event: { id }, deleted: true }); }, deleteError(id: string) { serverData.errors.delete(id); setTimeout(() => setServerData({ ...serverData }), 0); }, updateUser(user: UserDto) { - sock.updateUserData({ user, deleted: false }); + return sock.updateUserData({ user, deleted: false }); }, deleteUser(id: string) { - sock.updateUserData({ user: { id }, deleted: true }); + return sock.updateUserData({ user: { id }, deleted: true }); }, updateGroup(group: GroupDto) { - sock.updateGroupData({ group, deleted: false }); + return sock.updateGroupData({ group, deleted: false }); }, deleteGroup(id: string) { - sock.updateGroupData({ group: { id }, deleted: true }); + return sock.updateGroupData({ group: { id }, deleted: true }); }, updateOrganization(organization: OrganizationDto) { - sock.updateOrganizationData({ + return sock.updateOrganizationData({ organization, deleted: false, }); }, addManager(email: string, organizationId: string) { - sock.addManager({ email, organizationId }); + return sock.addManager({ email, organizationId }); }, deleteOrganization(id: string) { - sock.updateOrganizationData({ organization: { id }, deleted: true }); + return sock.updateOrganizationData({ + organization: { id }, + deleted: true, + }); }, }), [serverData, setServerData, sock] diff --git a/game/lib/api/game_server_api.dart b/game/lib/api/game_server_api.dart index f274e006..1066ef2e 100644 --- a/game/lib/api/game_server_api.dart +++ b/game/lib/api/game_server_api.dart @@ -45,7 +45,7 @@ class GameServerApi { completer.complete(arg); }; - Future.delayed(Duration(seconds: 2)) + Future.delayed(Duration(seconds: 5)) .then((value) => completer.complete(null)); _refreshEv = ev; diff --git a/scripts/updateapi-lib/dartgen.js b/scripts/updateapi-lib/dartgen.js index 899413b2..dd17478c 100644 --- a/scripts/updateapi-lib/dartgen.js +++ b/scripts/updateapi-lib/dartgen.js @@ -277,7 +277,7 @@ function getDartServerApiFile(apiDefs) { completer.complete(arg); }; - Future.delayed(Duration(seconds: 2)) + Future.delayed(Duration(seconds: 5)) .then((value) => completer.complete(null)); _refreshEv = ev; diff --git a/scripts/updateapi-lib/dartgen.ts b/scripts/updateapi-lib/dartgen.ts index 00bd2e4d..79aa28fc 100644 --- a/scripts/updateapi-lib/dartgen.ts +++ b/scripts/updateapi-lib/dartgen.ts @@ -286,7 +286,7 @@ export function getDartServerApiFile(apiDefs: ApiDefs) { completer.complete(arg); }; - Future.delayed(Duration(seconds: 2)) + Future.delayed(Duration(seconds: 5)) .then((value) => completer.complete(null)); _refreshEv = ev; diff --git a/scripts/updateapi-lib/tsgen.js b/scripts/updateapi-lib/tsgen.js index 6aebdd42..01b624bc 100644 --- a/scripts/updateapi-lib/tsgen.js +++ b/scripts/updateapi-lib/tsgen.js @@ -46,14 +46,15 @@ function getAdminApiFile(apiDefs) { send(ev: string, data: {}) { console.log(\`Sending \${ev} with \${JSON.stringify(data)}\`); - this.socket.emit(ev, data); + return this.socket.timeout(5000).emitWithAck(ev, data); } `; for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + const ackType = apiDefs.serverAcks.get(ev); tsCode += ` ${ev}(data: dto.${dto}) { - this.send("${ev}", data); + return this.send("${ev}", data) as Promise<${ackType} | undefined>; } `; diff --git a/scripts/updateapi-lib/tsgen.ts b/scripts/updateapi-lib/tsgen.ts index 2aa1b9e1..c7abcf23 100644 --- a/scripts/updateapi-lib/tsgen.ts +++ b/scripts/updateapi-lib/tsgen.ts @@ -55,15 +55,16 @@ export function getAdminApiFile(apiDefs: ApiDefs) { send(ev: string, data: {}) { console.log(\`Sending \${ev} with \${JSON.stringify(data)}\`); - this.socket.emit(ev, data); + return this.socket.timeout(5000).emitWithAck(ev, data); } `; for (const [ev, dto] of apiDefs.serverEntrypoints.entries()) { + const ackType = apiDefs.serverAcks.get(ev); tsCode += ` ${ev}(data: dto.${dto}) { - this.send("${ev}", data); + return this.send("${ev}", data) as Promise<${ackType} | undefined>; } `; From ca4a7d27fffe0f668d3c2e1bf6924e11acf8d17f Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 23:12:29 -0400 Subject: [PATCH 06/13] Fixed copy logic --- admin/src/components/Events.tsx | 9 +++++--- admin/src/components/ServerApi.tsx | 6 ++++++ admin/src/components/ServerData.tsx | 21 ++++++++++++++----- game/lib/api/game_server_api.dart | 4 ++++ server/src/achievement/achievement.gateway.ts | 9 ++++++-- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index ea6096c5..0572c930 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -208,7 +208,7 @@ function toForm(event: EventDto) { function makeCopyForm(orgOptions: string[], initialIndex: number) { return [ { - name: "Org", + name: "Target Organization", options: orgOptions, value: initialIndex, }, @@ -273,12 +273,15 @@ export function Events() { /> { const ev = serverData.events.get(currentId)!; const evId = await serverData.updateEvent({ ...ev, + challenges: [], + initialOrganizationId: + copyForm.orgIds[(copyForm.form[0] as OptionEntryForm).value], id: "", }); if (!evId) { @@ -298,7 +301,7 @@ export function Events() { onCancel={() => { setCopyModalOpen(false); }} - form={form} + form={copyForm.form} /> ; } + requestAchievementTrackerData(data: dto.RequestAchievementTrackerDataDto) { + return this.send("requestAchievementTrackerData", data) as Promise< + number | undefined + >; + } + updateAchievementData(data: dto.UpdateAchievementDataDto) { return this.send("updateAchievementData", data) as Promise< string | undefined diff --git a/admin/src/components/ServerData.tsx b/admin/src/components/ServerData.tsx index 2efca186..26167e72 100644 --- a/admin/src/components/ServerData.tsx +++ b/admin/src/components/ServerData.tsx @@ -45,20 +45,28 @@ const defaultData = { async deleteEvent(id: string): Promise { return undefined; }, + async updateAchievement( + achievement: AchievementDto + ): Promise { + return undefined; + }, + async deleteAchievement(id: string): Promise { + return undefined; + }, async updateOrganization( organization: OrganizationDto ): Promise { return undefined; }, + async deleteOrganization(id: string): Promise { + return undefined; + }, async addManager( email: string, orgniazationId: string ): Promise { return undefined; }, - async deleteOrganization(id: string): Promise { - return undefined; - }, deleteError(id: string) {}, async updateUser(user: UserDto): Promise { return undefined; @@ -119,10 +127,13 @@ export function ServerDataProvider(props: { children: ReactNode }) { return sock.updateEventData({ event: { id }, deleted: true }); }, updateAchievement(achievement: AchievementDto) { - sock.updateAchievementData({ achievement, deleted: false }); + return sock.updateAchievementData({ achievement, deleted: false }); }, deleteAchievement(id: string) { - sock.updateAchievementData({ achievement: { id }, deleted: true }); + return sock.updateAchievementData({ + achievement: { id }, + deleted: true, + }); }, deleteError(id: string) { serverData.errors.delete(id); diff --git a/game/lib/api/game_server_api.dart b/game/lib/api/game_server_api.dart index 1066ef2e..3dc1c3f0 100644 --- a/game/lib/api/game_server_api.dart +++ b/game/lib/api/game_server_api.dart @@ -61,6 +61,10 @@ class GameServerApi { Future requestAchievementData(RequestAchievementDataDto dto) async => await _invokeWithRefresh("requestAchievementData", dto.toJson()); + Future requestAchievementTrackerData( + RequestAchievementTrackerDataDto dto) async => + await _invokeWithRefresh("requestAchievementTrackerData", dto.toJson()); + Future updateAchievementData(UpdateAchievementDataDto dto) async => await _invokeWithRefresh("updateAchievementData", dto.toJson()); diff --git a/server/src/achievement/achievement.gateway.ts b/server/src/achievement/achievement.gateway.ts index 0672d8fc..ca6871c5 100644 --- a/server/src/achievement/achievement.gateway.ts +++ b/server/src/achievement/achievement.gateway.ts @@ -77,6 +77,8 @@ export class AchievementGateway { for (const ach of achs) { await this.achievementService.emitUpdateAchievementTracker(ach, user); } + + return achs.length; } /** @@ -131,7 +133,10 @@ export class AchievementGateway { data.achievement.initialOrganizationId!, ); - await this.orgService.emitUpdateOrganizationData(org, false); + if (org) { + await this.orgService.emitUpdateOrganizationData(org, false); + } + this.clientService.subscribe(user, achievement.id); await this.achievementService.emitUpdateAchievementData( achievement, @@ -139,6 +144,6 @@ export class AchievementGateway { ); } - return achievement.id; + return achievement?.id; } } From bc462dcd4addfa445810c35ea4db973cd0481158 Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 23:25:25 -0400 Subject: [PATCH 07/13] Finished duplications --- admin/src/components/Achievements.tsx | 56 +++++++++++++++++++++++++++ admin/src/components/Challenges.tsx | 54 ++++++++++++++++++++++++++ admin/src/components/Events.tsx | 4 +- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/admin/src/components/Achievements.tsx b/admin/src/components/Achievements.tsx index f60ee56e..f6dab953 100644 --- a/admin/src/components/Achievements.tsx +++ b/admin/src/components/Achievements.tsx @@ -52,6 +52,7 @@ function AchiemementCard(props: { onSelect: () => void; onEdit: () => void; onDelete: () => void; + onCopy: () => void; }) { return ( <> @@ -80,6 +81,9 @@ function AchiemementCard(props: { EDIT + + COPY + @@ -146,6 +150,16 @@ function toForm(achievement: AchievementDto) { ] as EntryForm[]; } +function makeCopyForm(orgOptions: string[], initialIndex: number) { + return [ + { + name: "Target Organization", + options: orgOptions, + value: initialIndex, + }, + ] as EntryForm[]; +} + export function Achievements() { const serverData = useContext(ServerDataContext); const [isCreateModalOpen, setCreateModalOpen] = useState(false); @@ -153,6 +167,13 @@ export function Achievements() { const [isDeleteModalOpen, setDeleteModalOpen] = useState(false); const [selectModalOpen, setSelectModalOpen] = useState(false); const [isLinkedModalOpen, setLinkedModalOpen] = useState(false); + + const [isCopyModalOpen, setCopyModalOpen] = useState(false); + const [copyForm, setCopyForm] = useState(() => ({ + form: makeCopyForm([], 0), + orgIds: [] as string[], + })); + const [form, setForm] = useState(() => makeForm()); const [currentId, setCurrentId] = useState(""); const [query, setQuery] = useState(""); @@ -203,6 +224,26 @@ export function Achievements() { }} form={form} /> + { + const ach = serverData.achievements.get(currentId)!; + serverData.updateAchievement({ + ...ach, + eventId: "", + initialOrganizationId: + copyForm.orgIds[(copyForm.form[0] as OptionEntryForm).value], + id: "", + }); + setCopyModalOpen(false); + }} + onCancel={() => { + setCopyModalOpen(false); + }} + form={copyForm.form} + /> { + const orgs = Array.from(serverData.organizations.values()); + const myOrgIndex = orgs.findIndex( + (v) => v.id === selectedOrg?.id + ); + setCurrentId(ach.id); + setCopyForm({ + form: makeCopyForm( + orgs.map((org) => org.name ?? ""), + myOrgIndex + ), + orgIds: orgs.map((org) => org.id), + }); + setCopyModalOpen(true); + }} /> ))} diff --git a/admin/src/components/Challenges.tsx b/admin/src/components/Challenges.tsx index a70da10d..d772fc5e 100644 --- a/admin/src/components/Challenges.tsx +++ b/admin/src/components/Challenges.tsx @@ -54,6 +54,7 @@ function ChallengeCard(props: { onDown: () => void; onDelete: () => void; onEdit: () => void; + onCopy: () => void; }) { return ( @@ -79,6 +80,9 @@ function ChallengeCard(props: { EDIT + + COPY + ); @@ -170,6 +174,16 @@ function fromForm( }; } +function makeCopyForm(evOptions: string[], initialIndex: number) { + return [ + { + name: "Target Event", + options: evOptions, + value: initialIndex, + }, + ] as EntryForm[]; +} + export function Challenges() { const [createModalOpen, setCreateModalOpen] = useState(false); const [editModalOpen, setEditModalOpen] = useState(false); @@ -180,6 +194,12 @@ export function Challenges() { const [currentId, setCurrentId] = useState(""); const [query, setQuery] = useState(""); + const [isCopyModalOpen, setCopyModalOpen] = useState(false); + const [copyForm, setCopyForm] = useState(() => ({ + form: makeCopyForm([], 0), + evIds: [] as string[], + })); + const serverData = useContext(ServerDataContext); const selectedEvent = serverData.events.get(serverData.selectedEvent); @@ -229,6 +249,25 @@ export function Challenges() { setDeleteModalOpen(false); }} /> + { + const chal = serverData.challenges.get(currentId)!; + serverData.updateChallenge({ + ...chal, + linkedEventId: + copyForm.evIds[(copyForm.form[0] as OptionEntryForm).value], + id: "", + }); + setCopyModalOpen(false); + }} + onCancel={() => { + setCopyModalOpen(false); + }} + form={copyForm.form} + /> { setForm(makeForm()); @@ -292,6 +331,21 @@ export function Challenges() { setCurrentId(chal.id); setDeleteModalOpen(true); }} + onCopy={() => { + const evs = Array.from(serverData.events.values()); + const myEvIndex = evs.findIndex( + (v) => v.id === selectedEvent?.id + ); + setCurrentId(chal.id); + setCopyForm({ + form: makeCopyForm( + evs.map((ev) => ev.name ?? ""), + myEvIndex + ), + evIds: evs.map((ev) => ev.id), + }); + setCopyModalOpen(true); + }} /> ))} diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index 0572c930..5371d240 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -221,12 +221,14 @@ export function Events() { const [isEditModalOpen, setEditModalOpen] = useState(false); const [isDeleteModalOpen, setDeleteModalOpen] = useState(false); const [selectModalOpen, setSelectModalOpen] = useState(false); + const [isCopyModalOpen, setCopyModalOpen] = useState(false); - const [form, setForm] = useState(() => makeForm()); const [copyForm, setCopyForm] = useState(() => ({ form: makeCopyForm([], 0), orgIds: [] as string[], })); + + const [form, setForm] = useState(() => makeForm()); const [currentId, setCurrentId] = useState(""); const [query, setQuery] = useState(""); const selectedOrg = serverData.organizations.get(serverData.selectedOrg); From 3fbf0e225f338c5ac21b08c90c192a1c6ba0bd11 Mon Sep 17 00:00:00 2001 From: neketka Date: Tue, 30 Apr 2024 23:49:36 -0400 Subject: [PATCH 08/13] Remove useless code --- server/src/challenge/challenge.service.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/src/challenge/challenge.service.ts b/server/src/challenge/challenge.service.ts index 4e47390c..31aa4b14 100644 --- a/server/src/challenge/challenge.service.ts +++ b/server/src/challenge/challenge.service.ts @@ -352,9 +352,6 @@ export class ChallengeService { accessibleBy(ability, Action.Delete).Challenge, ], }, - include: { - linkedEvent: { include: { challenges: true } }, - }, }); if (!challenge) return false; From 7ee1489df2ccbfcf3608403e0938d22e4705b72a Mon Sep 17 00:00:00 2001 From: neketka Date: Wed, 1 May 2024 00:12:25 -0400 Subject: [PATCH 09/13] Added text dialog --- game/lib/profile/settings_page.dart | 40 +++++++++++++++++++++++++-- game/lib/utils/utility_functions.dart | 36 ++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/game/lib/profile/settings_page.dart b/game/lib/profile/settings_page.dart index d124c56d..80b674cb 100644 --- a/game/lib/profile/settings_page.dart +++ b/game/lib/profile/settings_page.dart @@ -117,8 +117,44 @@ class SettingsPage extends StatelessWidget { Container( decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.all( - Radius.circular(10), + borderRadius: BorderRadius.only( + topRight: Radius.circular(10), + topLeft: Radius.circular(10), + ), + ), + child: TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + padding: EdgeInsets.only(left: 20.0), + alignment: Alignment.centerLeft, + fixedSize: Size(constraints.maxWidth, 60)), + child: Row( + children: [ + Padding( + padding: EdgeInsets.only(right: 20.0), + child: SvgPicture.asset( + 'assets/icons/head.svg', + ), + ), + Text( + 'Join Organization', + textAlign: TextAlign.left, + style: TextStyle( + fontFamily: 'Poppins', + fontSize: 16, + color: Colors.black), + ) + ], + ), + ), + ), + Divider(height: 1), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + bottomLeft: Radius.circular(10), ), ), child: TextButton( diff --git a/game/lib/utils/utility_functions.dart b/game/lib/utils/utility_functions.dart index 7ecaa98e..d042c6b3 100644 --- a/game/lib/utils/utility_functions.dart +++ b/game/lib/utils/utility_functions.dart @@ -1,3 +1,5 @@ +import 'dart:html'; + import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:game/api/game_api.dart'; @@ -33,6 +35,40 @@ Future showAlert(String message, context) async { ); } +TextEditingController _textFieldController = TextEditingController(); + +Future _displayTextInputDialog(BuildContext context, FunctionStringCallback onOk) async { + _textFieldController.clear(); + + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('TextField in Dialog'), + content: TextField( + controller: _textFieldController, + decoration: InputDecoration(hintText: "Text Field in Dialog"), + ), + actions: [ + TextButton( + child: Text('CANCEL'), + onPressed: () { + Navigator.pop(context); + }, + ), + TextButton( + child: Text('OK'), + onPressed: () { + onOk(_textFieldController.text) + Navigator.pop(context); + }, + ), + ], + ); + }, + ); +} + Future getId() async { var deviceInfo = DeviceInfoPlugin(); if (Platform.isIOS) { From a8cb5dc9c541b847fad8b03a104d0899b80214aa Mon Sep 17 00:00:00 2001 From: neketka Date: Wed, 1 May 2024 00:19:09 -0400 Subject: [PATCH 10/13] Add join org --- game/lib/profile/settings_page.dart | 14 +++++++++++++- game/lib/utils/utility_functions.dart | 11 +++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/game/lib/profile/settings_page.dart b/game/lib/profile/settings_page.dart index 80b674cb..098ff33b 100644 --- a/game/lib/profile/settings_page.dart +++ b/game/lib/profile/settings_page.dart @@ -1,7 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:game/api/game_api.dart'; +import 'package:game/api/game_client_dto.dart'; import 'package:game/profile/edit_profile.dart'; import 'package:game/main.dart'; +import 'package:game/utils/utility_functions.dart'; +import 'package:provider/provider.dart'; class SettingsPage extends StatelessWidget { final bool isGuest; @@ -123,7 +127,15 @@ class SettingsPage extends StatelessWidget { ), ), child: TextButton( - onPressed: () {}, + onPressed: () { + displayTextInputDialog( + context, "Join Organization", "Access Code", + (text) { + client.serverApi?.joinOrganization( + JoinOrganizationDto( + accessCode: text.toUpperCase())); + }); + }, style: TextButton.styleFrom( padding: EdgeInsets.only(left: 20.0), alignment: Alignment.centerLeft, diff --git a/game/lib/utils/utility_functions.dart b/game/lib/utils/utility_functions.dart index d042c6b3..f4703685 100644 --- a/game/lib/utils/utility_functions.dart +++ b/game/lib/utils/utility_functions.dart @@ -1,5 +1,3 @@ -import 'dart:html'; - import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:game/api/game_api.dart'; @@ -37,17 +35,18 @@ Future showAlert(String message, context) async { TextEditingController _textFieldController = TextEditingController(); -Future _displayTextInputDialog(BuildContext context, FunctionStringCallback onOk) async { +Future displayTextInputDialog( + BuildContext context, String text, String hintText, dynamic onOk) async { _textFieldController.clear(); return showDialog( context: context, builder: (context) { return AlertDialog( - title: Text('TextField in Dialog'), + title: Text(text), content: TextField( controller: _textFieldController, - decoration: InputDecoration(hintText: "Text Field in Dialog"), + decoration: InputDecoration(hintText: hintText), ), actions: [ TextButton( @@ -59,7 +58,7 @@ Future _displayTextInputDialog(BuildContext context, FunctionStringCallbac TextButton( child: Text('OK'), onPressed: () { - onOk(_textFieldController.text) + onOk(_textFieldController.text); Navigator.pop(context); }, ), From 45c46f21012aa9a29df09ef8ee1f3e735066c9b9 Mon Sep 17 00:00:00 2001 From: neketka Date: Wed, 1 May 2024 00:25:20 -0400 Subject: [PATCH 11/13] Added emit --- server/src/organization/organization.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/organization/organization.service.ts b/server/src/organization/organization.service.ts index 457cfc88..250830d8 100644 --- a/server/src/organization/organization.service.ts +++ b/server/src/organization/organization.service.ts @@ -376,5 +376,7 @@ export class OrganizationService { where: { id: org.id }, data: { members: { connect: { id: user.id } } }, }); + + await this.emitUpdateOrganizationData(org, false, user); } } From 8b76b4d8a98828669117d98328017032c74407ad Mon Sep 17 00:00:00 2001 From: cathli66 Date: Wed, 1 May 2024 01:16:40 -0400 Subject: [PATCH 12/13] users are allowed to see their own auth type --- server/src/casl/casl-ability.factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/casl/casl-ability.factory.ts b/server/src/casl/casl-ability.factory.ts index 0409585e..ae5aba81 100644 --- a/server/src/casl/casl-ability.factory.ts +++ b/server/src/casl/casl-ability.factory.ts @@ -131,7 +131,7 @@ export class CaslAbilityFactory { can( Action.Read, 'User', - ['email', 'groupId', 'id', 'score', 'trackedEvents', 'favorites'], + ['email', 'groupId', 'id', 'score', 'trackedEvents', 'favorites', 'authType'], { id: user.id, }, From 2bf793587a03b20a5796af653916c8de5ca68f0a Mon Sep 17 00:00:00 2001 From: cathli66 Date: Wed, 1 May 2024 01:19:32 -0400 Subject: [PATCH 13/13] fix formatting --- server/src/casl/casl-ability.factory.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/src/casl/casl-ability.factory.ts b/server/src/casl/casl-ability.factory.ts index ae5aba81..736523e0 100644 --- a/server/src/casl/casl-ability.factory.ts +++ b/server/src/casl/casl-ability.factory.ts @@ -131,7 +131,15 @@ export class CaslAbilityFactory { can( Action.Read, 'User', - ['email', 'groupId', 'id', 'score', 'trackedEvents', 'favorites', 'authType'], + [ + 'email', + 'groupId', + 'id', + 'score', + 'trackedEvents', + 'favorites', + 'authType', + ], { id: user.id, },