diff --git a/backend/internal/lobby/server.go b/backend/internal/lobby/server.go index 5de3a28..be2a645 100644 --- a/backend/internal/lobby/server.go +++ b/backend/internal/lobby/server.go @@ -1,6 +1,7 @@ package lobby import ( + "fmt" "github.com/gorilla/websocket" "github.com/mytja/Tarok/backend/internal/helpers" "github.com/mytja/Tarok/backend/internal/lobby_messages" @@ -399,6 +400,23 @@ func (s *serverImpl) Authenticated(client Client) { }, }) } + + replays, err := s.db.GetGamesByUserID(id) + if err != nil { + return + } + + for _, v := range replays { + client.Send(&lobby_messages.LobbyMessage{ + Data: &lobby_messages.LobbyMessage_Replay{ + Replay: &lobby_messages.Replay{ + Url: fmt.Sprintf("https://palcka.si/replay/%s?password=%s", v.GameID, v.Password), + GameId: v.GameID, + CreatedAt: v.CreatedAt, + }, + }, + }) + } } func (s *serverImpl) GetDB() sql.SQL { diff --git a/backend/internal/lobby_messages/lobby_messages.pb.go b/backend/internal/lobby_messages/lobby_messages.pb.go index 832f34f..d753d23 100644 --- a/backend/internal/lobby_messages/lobby_messages.pb.go +++ b/backend/internal/lobby_messages/lobby_messages.pb.go @@ -977,6 +977,69 @@ func (x *RemoveFriend) GetRelationshipId() string { return "" } +type Replay struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + GameId string `protobuf:"bytes,2,opt,name=gameId,proto3" json:"gameId,omitempty"` + CreatedAt string `protobuf:"bytes,3,opt,name=createdAt,proto3" json:"createdAt,omitempty"` +} + +func (x *Replay) Reset() { + *x = Replay{} + if protoimpl.UnsafeEnabled { + mi := &file_lobby_messages_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Replay) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Replay) ProtoMessage() {} + +func (x *Replay) ProtoReflect() protoreflect.Message { + mi := &file_lobby_messages_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Replay.ProtoReflect.Descriptor instead. +func (*Replay) Descriptor() ([]byte, []int) { + return file_lobby_messages_proto_rawDescGZIP(), []int{15} +} + +func (x *Replay) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *Replay) GetGameId() string { + if x != nil { + return x.GameId + } + return "" +} + +func (x *Replay) GetCreatedAt() string { + if x != nil { + return x.CreatedAt + } + return "" +} + type LobbyMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -999,13 +1062,14 @@ type LobbyMessage struct { // *LobbyMessage_FriendRequestAcceptDecline // *LobbyMessage_FriendRequestSend // *LobbyMessage_RemoveFriend + // *LobbyMessage_Replay Data isLobbyMessage_Data `protobuf_oneof:"data"` } func (x *LobbyMessage) Reset() { *x = LobbyMessage{} if protoimpl.UnsafeEnabled { - mi := &file_lobby_messages_proto_msgTypes[15] + mi := &file_lobby_messages_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1018,7 +1082,7 @@ func (x *LobbyMessage) String() string { func (*LobbyMessage) ProtoMessage() {} func (x *LobbyMessage) ProtoReflect() protoreflect.Message { - mi := &file_lobby_messages_proto_msgTypes[15] + mi := &file_lobby_messages_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1031,7 +1095,7 @@ func (x *LobbyMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use LobbyMessage.ProtoReflect.Descriptor instead. func (*LobbyMessage) Descriptor() ([]byte, []int) { - return file_lobby_messages_proto_rawDescGZIP(), []int{15} + return file_lobby_messages_proto_rawDescGZIP(), []int{16} } func (x *LobbyMessage) GetPlayerId() string { @@ -1146,6 +1210,13 @@ func (x *LobbyMessage) GetRemoveFriend() *RemoveFriend { return nil } +func (x *LobbyMessage) GetReplay() *Replay { + if x, ok := x.GetData().(*LobbyMessage_Replay); ok { + return x.Replay + } + return nil +} + type isLobbyMessage_Data interface { isLobbyMessage_Data() } @@ -1206,6 +1277,10 @@ type LobbyMessage_RemoveFriend struct { RemoveFriend *RemoveFriend `protobuf:"bytes,24,opt,name=remove_friend,json=removeFriend,proto3,oneof"` } +type LobbyMessage_Replay struct { + Replay *Replay `protobuf:"bytes,25,opt,name=replay,proto3,oneof"` +} + func (*LobbyMessage_LoginRequest) isLobbyMessage_Data() {} func (*LobbyMessage_LoginInfo) isLobbyMessage_Data() {} @@ -1234,6 +1309,8 @@ func (*LobbyMessage_FriendRequestSend) isLobbyMessage_Data() {} func (*LobbyMessage_RemoveFriend) isLobbyMessage_Data() {} +func (*LobbyMessage_Replay) isLobbyMessage_Data() {} + type LoginResponse_OK struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1243,7 +1320,7 @@ type LoginResponse_OK struct { func (x *LoginResponse_OK) Reset() { *x = LoginResponse_OK{} if protoimpl.UnsafeEnabled { - mi := &file_lobby_messages_proto_msgTypes[16] + mi := &file_lobby_messages_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1256,7 +1333,7 @@ func (x *LoginResponse_OK) String() string { func (*LoginResponse_OK) ProtoMessage() {} func (x *LoginResponse_OK) ProtoReflect() protoreflect.Message { - mi := &file_lobby_messages_proto_msgTypes[16] + mi := &file_lobby_messages_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1281,7 +1358,7 @@ type LoginResponse_Fail struct { func (x *LoginResponse_Fail) Reset() { *x = LoginResponse_Fail{} if protoimpl.UnsafeEnabled { - mi := &file_lobby_messages_proto_msgTypes[17] + mi := &file_lobby_messages_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1294,7 +1371,7 @@ func (x *LoginResponse_Fail) String() string { func (*LoginResponse_Fail) ProtoMessage() {} func (x *LoginResponse_Fail) ProtoReflect() protoreflect.Message { - mi := &file_lobby_messages_proto_msgTypes[17] + mi := &file_lobby_messages_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1319,7 +1396,7 @@ type Friend_Incoming struct { func (x *Friend_Incoming) Reset() { *x = Friend_Incoming{} if protoimpl.UnsafeEnabled { - mi := &file_lobby_messages_proto_msgTypes[18] + mi := &file_lobby_messages_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1332,7 +1409,7 @@ func (x *Friend_Incoming) String() string { func (*Friend_Incoming) ProtoMessage() {} func (x *Friend_Incoming) ProtoReflect() protoreflect.Message { - mi := &file_lobby_messages_proto_msgTypes[18] + mi := &file_lobby_messages_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1357,7 +1434,7 @@ type Friend_Outgoing struct { func (x *Friend_Outgoing) Reset() { *x = Friend_Outgoing{} if protoimpl.UnsafeEnabled { - mi := &file_lobby_messages_proto_msgTypes[19] + mi := &file_lobby_messages_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1370,7 +1447,7 @@ func (x *Friend_Outgoing) String() string { func (*Friend_Outgoing) ProtoMessage() {} func (x *Friend_Outgoing) ProtoReflect() protoreflect.Message { - mi := &file_lobby_messages_proto_msgTypes[19] + mi := &file_lobby_messages_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1395,7 +1472,7 @@ type Friend_Connected struct { func (x *Friend_Connected) Reset() { *x = Friend_Connected{} if protoimpl.UnsafeEnabled { - mi := &file_lobby_messages_proto_msgTypes[20] + mi := &file_lobby_messages_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1408,7 +1485,7 @@ func (x *Friend_Connected) String() string { func (*Friend_Connected) ProtoMessage() {} func (x *Friend_Connected) ProtoReflect() protoreflect.Message { - mi := &file_lobby_messages_proto_msgTypes[20] + mi := &file_lobby_messages_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1530,74 +1607,82 @@ var file_lobby_messages_proto_rawDesc = []byte{ 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x49, 0x64, 0x22, - 0x88, 0x08, 0x0a, 0x0c, 0x4c, 0x6f, 0x62, 0x62, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x43, 0x0a, - 0x0d, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, - 0x6f, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, - 0x0a, 0x0e, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x67, 0x61, 0x6d, 0x65, 0x5f, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, - 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, - 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x67, 0x61, 0x6d, - 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x0e, 0x67, 0x61, 0x6d, 0x65, - 0x5f, 0x64, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x44, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x48, - 0x00, 0x52, 0x0d, 0x67, 0x61, 0x6d, 0x65, 0x44, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, 0x65, 0x64, - 0x12, 0x37, 0x0a, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x48, 0x00, 0x52, - 0x08, 0x67, 0x61, 0x6d, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0a, 0x67, 0x61, 0x6d, - 0x65, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, - 0x61, 0x6d, 0x65, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x48, 0x00, 0x52, 0x09, 0x67, 0x61, 0x6d, 0x65, - 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x6f, - 0x76, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, - 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x4d, 0x6f, - 0x76, 0x65, 0x48, 0x00, 0x52, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x4d, 0x6f, 0x76, 0x65, 0x12, 0x3d, - 0x0a, 0x0b, 0x67, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x48, - 0x00, 0x52, 0x0a, 0x67, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, 0x56, 0x0a, - 0x14, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x6f, - 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x69, - 0x65, 0x6e, 0x64, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, - 0x00, 0x52, 0x12, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x48, 0x00, 0x52, - 0x06, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x6f, 0x0a, 0x1d, 0x66, 0x72, 0x69, 0x65, 0x6e, - 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x50, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x67, + 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x61, 0x6d, + 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x22, 0xba, 0x08, 0x0a, 0x0c, 0x4c, 0x6f, 0x62, 0x62, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x43, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x46, 0x0a, 0x0e, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x6c, 0x6f, 0x67, 0x69, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x67, 0x61, 0x6d, 0x65, + 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, - 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x1a, 0x66, 0x72, - 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x53, 0x0a, 0x13, 0x66, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x18, - 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x11, 0x66, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x43, 0x0a, - 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x18, 0x18, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x23, 0x5a, 0x21, 0x2e, 0x2f, - 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x47, 0x61, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x67, + 0x61, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x0e, 0x67, 0x61, + 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x44, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, 0x65, + 0x64, 0x48, 0x00, 0x52, 0x0d, 0x67, 0x61, 0x6d, 0x65, 0x44, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, + 0x65, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x48, + 0x00, 0x52, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0a, 0x67, + 0x61, 0x6d, 0x65, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x48, 0x00, 0x52, 0x09, 0x67, 0x61, + 0x6d, 0x65, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x5f, + 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x62, + 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, + 0x4d, 0x6f, 0x76, 0x65, 0x48, 0x00, 0x52, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x4d, 0x6f, 0x76, 0x65, + 0x12, 0x3d, 0x0a, 0x0b, 0x67, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x48, 0x00, 0x52, 0x0a, 0x67, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, + 0x56, 0x0a, 0x14, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x46, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x48, 0x00, 0x52, 0x12, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x72, 0x69, 0x65, 0x6e, + 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x48, + 0x00, 0x52, 0x06, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x6f, 0x0a, 0x1d, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x73, 0x2e, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x1a, + 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x53, 0x0a, 0x13, 0x66, 0x72, + 0x69, 0x65, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, + 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x11, 0x66, 0x72, + 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x12, + 0x43, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, + 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, + 0x69, 0x65, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, + 0x69, 0x65, 0x6e, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x19, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x06, + 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x23, + 0x5a, 0x21, 0x2e, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x6f, 0x62, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1612,7 +1697,7 @@ func file_lobby_messages_proto_rawDescGZIP() []byte { return file_lobby_messages_proto_rawDescData } -var file_lobby_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 21) +var file_lobby_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_lobby_messages_proto_goTypes = []interface{}{ (*LoginRequest)(nil), // 0: lobby_messages.LoginRequest (*LoginInfo)(nil), // 1: lobby_messages.LoginInfo @@ -1629,22 +1714,23 @@ var file_lobby_messages_proto_goTypes = []interface{}{ (*FriendRequestAcceptDecline)(nil), // 12: lobby_messages.FriendRequestAcceptDecline (*FriendRequestSend)(nil), // 13: lobby_messages.FriendRequestSend (*RemoveFriend)(nil), // 14: lobby_messages.RemoveFriend - (*LobbyMessage)(nil), // 15: lobby_messages.LobbyMessage - (*LoginResponse_OK)(nil), // 16: lobby_messages.LoginResponse.OK - (*LoginResponse_Fail)(nil), // 17: lobby_messages.LoginResponse.Fail - (*Friend_Incoming)(nil), // 18: lobby_messages.Friend.Incoming - (*Friend_Outgoing)(nil), // 19: lobby_messages.Friend.Outgoing - (*Friend_Connected)(nil), // 20: lobby_messages.Friend.Connected + (*Replay)(nil), // 15: lobby_messages.Replay + (*LobbyMessage)(nil), // 16: lobby_messages.LobbyMessage + (*LoginResponse_OK)(nil), // 17: lobby_messages.LoginResponse.OK + (*LoginResponse_Fail)(nil), // 18: lobby_messages.LoginResponse.Fail + (*Friend_Incoming)(nil), // 19: lobby_messages.Friend.Incoming + (*Friend_Outgoing)(nil), // 20: lobby_messages.Friend.Outgoing + (*Friend_Connected)(nil), // 21: lobby_messages.Friend.Connected } var file_lobby_messages_proto_depIdxs = []int32{ - 16, // 0: lobby_messages.LoginResponse.ok:type_name -> lobby_messages.LoginResponse.OK - 17, // 1: lobby_messages.LoginResponse.fail:type_name -> lobby_messages.LoginResponse.Fail + 17, // 0: lobby_messages.LoginResponse.ok:type_name -> lobby_messages.LoginResponse.OK + 18, // 1: lobby_messages.LoginResponse.fail:type_name -> lobby_messages.LoginResponse.Fail 3, // 2: lobby_messages.GameCreated.players:type_name -> lobby_messages.Player 3, // 3: lobby_messages.GameJoin.player:type_name -> lobby_messages.Player 3, // 4: lobby_messages.GameLeave.player:type_name -> lobby_messages.Player - 20, // 5: lobby_messages.Friend.connected:type_name -> lobby_messages.Friend.Connected - 19, // 6: lobby_messages.Friend.outgoing:type_name -> lobby_messages.Friend.Outgoing - 18, // 7: lobby_messages.Friend.incoming:type_name -> lobby_messages.Friend.Incoming + 21, // 5: lobby_messages.Friend.connected:type_name -> lobby_messages.Friend.Connected + 20, // 6: lobby_messages.Friend.outgoing:type_name -> lobby_messages.Friend.Outgoing + 19, // 7: lobby_messages.Friend.incoming:type_name -> lobby_messages.Friend.Incoming 0, // 8: lobby_messages.LobbyMessage.login_request:type_name -> lobby_messages.LoginRequest 1, // 9: lobby_messages.LobbyMessage.login_info:type_name -> lobby_messages.LoginInfo 2, // 10: lobby_messages.LobbyMessage.login_response:type_name -> lobby_messages.LoginResponse @@ -1659,11 +1745,12 @@ var file_lobby_messages_proto_depIdxs = []int32{ 12, // 19: lobby_messages.LobbyMessage.friend_request_accept_decline:type_name -> lobby_messages.FriendRequestAcceptDecline 13, // 20: lobby_messages.LobbyMessage.friend_request_send:type_name -> lobby_messages.FriendRequestSend 14, // 21: lobby_messages.LobbyMessage.remove_friend:type_name -> lobby_messages.RemoveFriend - 22, // [22:22] is the sub-list for method output_type - 22, // [22:22] is the sub-list for method input_type - 22, // [22:22] is the sub-list for extension type_name - 22, // [22:22] is the sub-list for extension extendee - 0, // [0:22] is the sub-list for field type_name + 15, // 22: lobby_messages.LobbyMessage.replay:type_name -> lobby_messages.Replay + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name } func init() { file_lobby_messages_proto_init() } @@ -1853,7 +1940,7 @@ func file_lobby_messages_proto_init() { } } file_lobby_messages_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LobbyMessage); i { + switch v := v.(*Replay); i { case 0: return &v.state case 1: @@ -1865,7 +1952,7 @@ func file_lobby_messages_proto_init() { } } file_lobby_messages_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginResponse_OK); i { + switch v := v.(*LobbyMessage); i { case 0: return &v.state case 1: @@ -1877,7 +1964,7 @@ func file_lobby_messages_proto_init() { } } file_lobby_messages_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginResponse_Fail); i { + switch v := v.(*LoginResponse_OK); i { case 0: return &v.state case 1: @@ -1889,7 +1976,7 @@ func file_lobby_messages_proto_init() { } } file_lobby_messages_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Friend_Incoming); i { + switch v := v.(*LoginResponse_Fail); i { case 0: return &v.state case 1: @@ -1901,7 +1988,7 @@ func file_lobby_messages_proto_init() { } } file_lobby_messages_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Friend_Outgoing); i { + switch v := v.(*Friend_Incoming); i { case 0: return &v.state case 1: @@ -1913,6 +2000,18 @@ func file_lobby_messages_proto_init() { } } file_lobby_messages_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Friend_Outgoing); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_lobby_messages_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Friend_Connected); i { case 0: return &v.state @@ -1934,7 +2033,7 @@ func file_lobby_messages_proto_init() { (*Friend_Outgoing_)(nil), (*Friend_Incoming_)(nil), } - file_lobby_messages_proto_msgTypes[15].OneofWrappers = []interface{}{ + file_lobby_messages_proto_msgTypes[16].OneofWrappers = []interface{}{ (*LobbyMessage_LoginRequest)(nil), (*LobbyMessage_LoginInfo)(nil), (*LobbyMessage_LoginResponse)(nil), @@ -1949,6 +2048,7 @@ func file_lobby_messages_proto_init() { (*LobbyMessage_FriendRequestAcceptDecline)(nil), (*LobbyMessage_FriendRequestSend)(nil), (*LobbyMessage_RemoveFriend)(nil), + (*LobbyMessage_Replay)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -1956,7 +2056,7 @@ func file_lobby_messages_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_lobby_messages_proto_rawDesc, NumEnums: 0, - NumMessages: 21, + NumMessages: 22, NumExtensions: 0, NumServices: 0, }, diff --git a/backend/internal/sql/game.go b/backend/internal/sql/game.go index 1365c47..8301a70 100644 --- a/backend/internal/sql/game.go +++ b/backend/internal/sql/game.go @@ -16,6 +16,11 @@ func (db *sqlImpl) GetGame(id string) (game Game, err error) { return game, err } +func (db *sqlImpl) GetGamesByUserID(id string) (games []Game, err error) { + err = db.db.Select(&games, "SELECT * FROM game_user WHERE user_id=$1 ORDER BY created_at DESC", id) + return games, err +} + func (db *sqlImpl) InsertGame(game Game) (err error) { _, err = db.db.NamedExec( "INSERT INTO game_user (user_id, game_id, messages, password) VALUES (:user_id, :game_id, :messages, :password)", diff --git a/backend/internal/sql/sql.go b/backend/internal/sql/sql.go index 573114c..e056fd3 100644 --- a/backend/internal/sql/sql.go +++ b/backend/internal/sql/sql.go @@ -49,6 +49,7 @@ type SQL interface { DeleteFriends(ID string) error GetGame(id string) (game Game, err error) + GetGamesByUserID(id string) (games []Game, err error) InsertGame(game Game) (err error) GetGames() (games []Game, err error) } diff --git a/backend/internal/ws/replay.go b/backend/internal/ws/replay.go index d3b1192..1b555fc 100644 --- a/backend/internal/ws/replay.go +++ b/backend/internal/ws/replay.go @@ -95,6 +95,11 @@ func (s *serverImpl) NextReplayStep(gameId string) { return } + if len(game.ReplayMessages) <= game.ReplayGame { + s.logger.Debugw("ne morem pridobiti igre", "replayState", game.ReplayState, "g", game.ReplayGame, "msg", len(game.ReplayMessages)) + return + } + g := game.ReplayMessages[game.ReplayGame] game.ReplayState++ diff --git a/messages/lobby_messages.proto b/messages/lobby_messages.proto index 2494a5b..6c0f68c 100644 --- a/messages/lobby_messages.proto +++ b/messages/lobby_messages.proto @@ -86,6 +86,12 @@ message FriendRequestAcceptDecline { message FriendRequestSend { string email = 1; } message RemoveFriend { string relationshipId = 1; } +message Replay { + string url = 1; + string gameId = 2; + string createdAt = 3; +} + message LobbyMessage { string player_id = 1; @@ -105,5 +111,7 @@ message LobbyMessage { FriendRequestAcceptDecline friend_request_accept_decline = 22; FriendRequestSend friend_request_send = 23; RemoveFriend remove_friend = 24; + + Replay replay = 25; } } diff --git a/tarok/lib/friends.dart b/tarok/lib/friends.dart index ab66d66..a59ce23 100644 --- a/tarok/lib/friends.dart +++ b/tarok/lib/friends.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart' hide FormData; import 'package:tarok/constants.dart'; import 'package:tarok/game_controller.dart'; import 'package:tarok/lobby_controller.dart'; +import 'package:tarok/ui/main_page.dart'; class Friends extends StatelessWidget { const Friends({super.key, this.gameId = ""}); @@ -16,299 +17,237 @@ class Friends extends StatelessWidget { List invited = []; - Widget mainContent = Center( - child: Obx( - () => ListView( - shrinkWrap: true, - children: [ - const Center( - child: Text( - 'Moji prijatelji', - style: TextStyle(fontSize: 40), - ), + Widget mainContent = Obx( + () => ListView( + shrinkWrap: true, + children: [ + const Center( + child: Text( + 'Moji prijatelji', + style: TextStyle(fontSize: 40), ), - const Center( - child: Text( - 'Prihodne prošnje za prijateljstvo', - style: TextStyle(fontSize: 20), - ), + ), + const Center( + child: Text( + 'Prihodne prošnje za prijateljstvo', + style: TextStyle(fontSize: 20), ), - ...controller.prihodne.map( - (e) => Container( - margin: const EdgeInsets.all(5.0), - child: Row( - children: [ - const SizedBox( - width: 100, - ), - Initicon( - text: e.name, - elevation: 4, - size: 70, - backgroundColor: - HSLColor.fromAHSL(1, hashCode(e.name) % 360, 1, 0.6) - .toColor(), - borderRadius: BorderRadius.zero, - ), - const SizedBox( - width: 20, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(e.name, - style: const TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - )), - Text(e.email), - ], + ), + ...controller.prihodne.map( + (e) => Container( + margin: const EdgeInsets.all(5.0), + child: Row( + children: [ + const SizedBox( + width: 100, + ), + Initicon( + text: e.name, + elevation: 4, + size: 70, + backgroundColor: + HSLColor.fromAHSL(1, hashCode(e.name) % 360, 1, 0.6) + .toColor(), + borderRadius: BorderRadius.zero, + ), + const SizedBox( + width: 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(e.name, + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + )), + Text(e.email), + ], + ), + const Spacer(), + if (gameId == "") + IconButton( + icon: const Icon(Icons.check), + onPressed: () async { + await controller.friendRequest(e.relationshipId, true); + }, ), - const Spacer(), - if (gameId == "") - IconButton( - icon: const Icon(Icons.check), - onPressed: () async { - await controller.friendRequest( - e.relationshipId, true); - }, - ), - if (gameId == "") - IconButton( - icon: const Icon(Icons.block), - onPressed: () async { - await controller.friendRequest( - e.relationshipId, false); - }, - ), - const SizedBox( - width: 100, + if (gameId == "") + IconButton( + icon: const Icon(Icons.block), + onPressed: () async { + await controller.friendRequest(e.relationshipId, false); + }, ), - ], - ), + const SizedBox( + width: 100, + ), + ], ), ), - const Center( - child: Text( - 'Odhodne prošnje za prijateljstvo', - style: TextStyle(fontSize: 20), - ), + ), + const Center( + child: Text( + 'Odhodne prošnje za prijateljstvo', + style: TextStyle(fontSize: 20), ), - ...controller.odhodne.map( - (e) => Container( - margin: const EdgeInsets.all(5.0), - child: Row( - children: [ - const SizedBox( - width: 100, - ), - Initicon( - text: e.name, - elevation: 4, - size: 70, - backgroundColor: - HSLColor.fromAHSL(1, hashCode(e.name) % 360, 1, 0.6) - .toColor(), - borderRadius: BorderRadius.zero, - ), - const SizedBox( - width: 20, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(e.name, - style: const TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - )), - Text(e.email), - ], - ), - const Spacer(), - if (gameId == "") - IconButton( - icon: const Icon(Icons.cancel), - onPressed: () async { - await controller.removeRelation(e.relationshipId); - }, - ), - const SizedBox( - width: 100, + ), + ...controller.odhodne.map( + (e) => Container( + margin: const EdgeInsets.all(5.0), + child: Row( + children: [ + const SizedBox( + width: 100, + ), + Initicon( + text: e.name, + elevation: 4, + size: 70, + backgroundColor: + HSLColor.fromAHSL(1, hashCode(e.name) % 360, 1, 0.6) + .toColor(), + borderRadius: BorderRadius.zero, + ), + const SizedBox( + width: 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(e.name, + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + )), + Text(e.email), + ], + ), + const Spacer(), + if (gameId == "") + IconButton( + icon: const Icon(Icons.cancel), + onPressed: () async { + await controller.removeRelation(e.relationshipId); + }, ), - ], - ), + const SizedBox( + width: 100, + ), + ], ), ), - const Center( - child: Text( - 'Prijatelji', - style: TextStyle(fontSize: 20), - ), + ), + const Center( + child: Text( + 'Prijatelji', + style: TextStyle(fontSize: 20), ), - ...controller.prijatelji.map( - (e) => Container( - margin: const EdgeInsets.all(5.0), - child: Row( - children: [ - const SizedBox( - width: 100, - ), - SizedBox( - height: 70, - width: 70, - child: Stack(children: [ - Initicon( - text: e.name, - elevation: 4, - size: 70, - backgroundColor: HSLColor.fromAHSL( - 1, hashCode(e.name) % 360, 1, 0.6) - .toColor(), - borderRadius: BorderRadius.zero, - ), - if (e.status == 0) - Align( - alignment: Alignment.bottomRight, - child: Container( - color: Colors.grey, - height: 15, - width: 15, - ), + ), + ...controller.prijatelji.map( + (e) => Container( + margin: const EdgeInsets.all(5.0), + child: Row( + children: [ + const SizedBox( + width: 100, + ), + SizedBox( + height: 70, + width: 70, + child: Stack(children: [ + Initicon( + text: e.name, + elevation: 4, + size: 70, + backgroundColor: + HSLColor.fromAHSL(1, hashCode(e.name) % 360, 1, 0.6) + .toColor(), + borderRadius: BorderRadius.zero, + ), + if (e.status == 0) + Align( + alignment: Alignment.bottomRight, + child: Container( + color: Colors.grey, + height: 15, + width: 15, ), - if (e.status == 1) - Align( - alignment: Alignment.bottomRight, - child: Container( - color: Colors.greenAccent.shade700, - height: 15, - width: 15, - ), + ), + if (e.status == 1) + Align( + alignment: Alignment.bottomRight, + child: Container( + color: Colors.greenAccent.shade700, + height: 15, + width: 15, ), - if (e.status == 2) - Align( - alignment: Alignment.bottomRight, - child: Container( - color: Colors.red, - height: 15, - width: 15, - ), + ), + if (e.status == 2) + Align( + alignment: Alignment.bottomRight, + child: Container( + color: Colors.red, + height: 15, + width: 15, ), - ]), - ), - const SizedBox( - width: 20, + ), + ]), + ), + const SizedBox( + width: 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(e.name, + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + )), + Text(e.email), + ], + ), + const Spacer(), + if (gameId == "") + IconButton( + icon: const Icon(Icons.remove), + onPressed: () async { + await controller.removeRelation(e.relationshipId); + }, ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(e.name, - style: const TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - )), - Text(e.email), - ], + if (gameId != "" && !invited.contains(e.id)) + ElevatedButton( + child: const Text("Povabi"), + onPressed: () async { + GameController c = Get.put(GameController()); + await c.invitePlayer(e.id); + invited.add(e.id); + controller.prijatelji.refresh(); + }, ), - const Spacer(), - if (gameId == "") - IconButton( - icon: const Icon(Icons.remove), - onPressed: () async { - await controller.removeRelation(e.relationshipId); - }, - ), - if (gameId != "" && !invited.contains(e.id)) - ElevatedButton( - child: const Text("Povabi"), - onPressed: () async { - GameController c = Get.put(GameController()); - await c.invitePlayer(e.id); - invited.add(e.id); - controller.prijatelji.refresh(); - }, - ), - if (gameId != "" && invited.contains(e.id)) - const Icon(Icons.check), - const SizedBox( - width: 100, - ), - ], - ), + if (gameId != "" && invited.contains(e.id)) + const Icon(Icons.check), + const SizedBox( + width: 100, + ), + ], ), ), - ], - ), + ), + ], ), ); if (gameId != "") return mainContent; - return Scaffold( - drawer: Drawer( - child: ListView( - padding: EdgeInsets.zero, - children: [ - DrawerHeader( - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - ), - child: const Text( - 'Palčka.si tarok program', - style: TextStyle( - color: Colors.white, - fontSize: 24, - ), - ), - ), - ListTile( - leading: const Icon(Icons.home), - title: const Text('Domov'), - onTap: () { - Get.toNamed("/"); - }, - ), - ListTile( - leading: const Icon(Icons.people), - title: const Text('Prijatelji'), - onTap: () { - Get.toNamed("/friends"); - }, - ), - ], - ), - ), - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: const Text("Palčka"), - actions: [ - IconButton( - icon: const Icon(Icons.settings), - onPressed: () async { - Get.toNamed("/settings"); - }, - ), - IconButton( - icon: const Icon(Icons.info), - onPressed: () async { - Get.toNamed("/about"); - }, - ), - IconButton( - icon: const Icon(Icons.logout), - onPressed: () async { - await storage.deleteAll(); - Get.toNamed("/login"); - }, - ), - ], - ), - body: mainContent, + return PalckaHome( floatingActionButton: FloatingActionButton( onPressed: controller.friendAddDialog, tooltip: 'Dodaj prijatelja', child: const Icon(Icons.add), ), + child: mainContent, ); } } diff --git a/tarok/lib/lobby.dart b/tarok/lib/lobby.dart index ef95b52..1827665 100644 --- a/tarok/lib/lobby.dart +++ b/tarok/lib/lobby.dart @@ -5,6 +5,7 @@ import 'package:stockskis/stockskis.dart' hide Card, debugPrint; import 'package:tarok/constants.dart'; import 'package:tarok/lobby_controller.dart'; import 'package:tarok/replay.dart'; +import 'package:tarok/ui/main_page.dart'; Future preloadCards(BuildContext context) async { for (int i = 0; i < CARDS.length; i++) { @@ -23,667 +24,604 @@ class Lobby extends StatelessWidget { Widget build(BuildContext context) { LobbyController controller = Get.put(LobbyController()); - return Scaffold( - drawer: Drawer( - child: ListView( - padding: EdgeInsets.zero, + return PalckaHome( + floatingActionButton: FloatingActionButton( + onPressed: controller.dialog, + tooltip: 'Ustvari novo igro', + child: const Icon(Icons.add), + ), + child: Obx( + () => ListView( + shrinkWrap: true, children: [ - DrawerHeader( - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - ), - child: const Text( - 'Palčka.si tarok program', - style: TextStyle( - color: Colors.white, - fontSize: 24, - ), + const Center( + child: Text( + 'Dobrodošli v Palčka tarok programu.', + style: TextStyle(fontSize: 40), ), ), - ListTile( - leading: const Icon(Icons.home), - title: const Text('Domov'), - onTap: () { - Get.toNamed("/"); - }, - ), - ListTile( - leading: const Icon(Icons.people), - title: const Text('Prijatelji'), - onTap: () { - Get.toNamed("/friends"); - }, - ), - ], - ), - ), - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: const Text("Palčka"), - actions: [ - IconButton( - icon: const Icon(Icons.settings), - onPressed: () async { - Get.toNamed("/settings"); - }, - ), - IconButton( - icon: const Icon(Icons.info), - onPressed: () async { - Get.toNamed("/about"); - }, - ), - IconButton( - icon: const Icon(Icons.logout), - onPressed: () async { - await storage.deleteAll(); - Get.toNamed("/login"); - }, - ), - ], - ), - body: Center( - child: Obx( - () => ListView( - shrinkWrap: true, - children: [ + if (controller.guest.value) const Center( child: Text( - 'Dobrodošli v Palčka tarok programu.', - style: TextStyle(fontSize: 40), + "Uporabljate gostujoči dostop", + style: TextStyle(fontSize: 20), ), ), - if (controller.guest.value) - const Center( - child: Text( - "Uporabljate gostujoči dostop", - style: TextStyle(fontSize: 20), - ), - ), - const Center( - child: Text( - "Igre na voljo", - style: TextStyle(fontSize: 30), - ), + const Center( + child: Text( + "Igre na voljo", + style: TextStyle(fontSize: 30), ), - if (!controller.guest.value) - Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("S pravimi igralci"), - const SizedBox( - width: 10, - ), - ElevatedButton.icon( - onPressed: () => controller.quickGameFind(3, "normal"), - label: const Text( - "V tri", - style: TextStyle( - fontSize: 20, - ), - ), - icon: const Icon(Icons.face), - ), - ElevatedButton.icon( - onPressed: () => controller.quickGameFind(4, "normal"), - label: const Text( - "V štiri", - style: TextStyle( - fontSize: 20, - ), - ), - icon: const Icon(Icons.face), - ), - ]), - if (!controller.guest.value) - Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("Klepetalnica"), - const SizedBox( - width: 10, - ), - ElevatedButton.icon( - onPressed: () => - controller.quickGameFind(3, "klepetalnica"), - label: const Text( - "V tri", - style: TextStyle( - fontSize: 20, - ), + ), + if (!controller.guest.value) + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + const Text("S pravimi igralci"), + const SizedBox( + width: 10, + ), + ElevatedButton.icon( + onPressed: () => controller.quickGameFind(3, "normal"), + label: const Text( + "V tri", + style: TextStyle( + fontSize: 20, ), - icon: const Icon(Icons.face), ), - ElevatedButton.icon( - onPressed: () => - controller.quickGameFind(4, "klepetalnica"), - label: const Text( - "V štiri", - style: TextStyle( - fontSize: 20, - ), + icon: const Icon(Icons.face), + ), + ElevatedButton.icon( + onPressed: () => controller.quickGameFind(4, "normal"), + label: const Text( + "V štiri", + style: TextStyle( + fontSize: 20, ), - icon: const Icon(Icons.face), ), - ]), + icon: const Icon(Icons.face), + ), + ]), + if (!controller.guest.value) Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("Z računalniškimi igralci"), + const Text("Klepetalnica"), const SizedBox( width: 10, ), ElevatedButton.icon( - onPressed: () => controller.botGame(3), + onPressed: () => controller.quickGameFind(3, "klepetalnica"), label: const Text( "V tri", style: TextStyle( fontSize: 20, ), ), - icon: const Icon(Icons.smart_toy), + icon: const Icon(Icons.face), ), ElevatedButton.icon( - onPressed: () => controller.botGame(4), + onPressed: () => controller.quickGameFind(4, "klepetalnica"), label: const Text( "V štiri", style: TextStyle( fontSize: 20, ), ), - icon: const Icon(Icons.smart_toy), + icon: const Icon(Icons.face), ), ]), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + const Text("Z računalniškimi igralci"), const SizedBox( - height: 10, + width: 10, ), - if (controller.isAdmin.value) - Center( - child: ElevatedButton( - onPressed: () => Get.defaultDialog( - title: 'Administratorska plošča', - content: SingleChildScrollView( - child: Column(children: [ - const Text( - 'Na tej plošči lahko kot administrator urejate razne nastavitve tarok programa Palčka', - ), - const SizedBox( - height: 10, - ), - FutureBuilder( - future: controller.getRegistrationCodes(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return DataTable( - columns: const [ - DataColumn( - label: Expanded( - child: Text( - 'Registracijska koda', - style: TextStyle( - fontStyle: FontStyle.italic), - ), + ElevatedButton.icon( + onPressed: () => controller.botGame(3), + label: const Text( + "V tri", + style: TextStyle( + fontSize: 20, + ), + ), + icon: const Icon(Icons.smart_toy), + ), + ElevatedButton.icon( + onPressed: () => controller.botGame(4), + label: const Text( + "V štiri", + style: TextStyle( + fontSize: 20, + ), + ), + icon: const Icon(Icons.smart_toy), + ), + ]), + const SizedBox( + height: 10, + ), + if (controller.isAdmin.value) + Center( + child: ElevatedButton( + onPressed: () => Get.defaultDialog( + title: 'Administratorska plošča', + content: SingleChildScrollView( + child: Column(children: [ + const Text( + 'Na tej plošči lahko kot administrator urejate razne nastavitve tarok programa Palčka', + ), + const SizedBox( + height: 10, + ), + FutureBuilder( + future: controller.getRegistrationCodes(), + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return DataTable( + columns: const [ + DataColumn( + label: Expanded( + child: Text( + 'Registracijska koda', + style: TextStyle( + fontStyle: FontStyle.italic), ), ), - DataColumn( - label: Expanded( - child: Text( - 'Izbriši', - style: TextStyle( - fontStyle: FontStyle.italic), - ), + ), + DataColumn( + label: Expanded( + child: Text( + 'Izbriši', + style: TextStyle( + fontStyle: FontStyle.italic), ), ), - ], - rows: [ - ...controller.codes.map( - (code) => DataRow( - cells: [ - DataCell(Text(code["Code"])), - DataCell( - IconButton( - icon: const Icon(Icons.delete), - onPressed: () async { - await dio.delete( - "$BACKEND_URL/admin/reg_code", - data: FormData.fromMap( - { - "code": code["Code"], - }, - ), - options: Options( - headers: { - "X-Login-Token": - await storage.read( - key: "token") - }, - ), - ); - controller - .getRegistrationCodes(); - }, - ), + ), + ], + rows: [ + ...controller.codes.map( + (code) => DataRow( + cells: [ + DataCell(Text(code["Code"])), + DataCell( + IconButton( + icon: const Icon(Icons.delete), + onPressed: () async { + await dio.delete( + "$BACKEND_URL/admin/reg_code", + data: FormData.fromMap( + { + "code": code["Code"], + }, + ), + options: Options( + headers: { + "X-Login-Token": + await storage.read( + key: "token") + }, + ), + ); + controller.getRegistrationCodes(); + }, ), - ], - ), + ), + ], ), - ], - ); - } - return const SizedBox(); - }, - ), - Row(children: [ - Expanded( - child: TextField( - controller: controller.controller.value, - decoration: const InputDecoration( - border: UnderlineInputBorder(), - labelText: 'Nova registracijska koda', - ), - ), - ), - IconButton( - icon: const Icon(Icons.save), - onPressed: () async { - await dio.post( - "$BACKEND_URL/admin/reg_code", - data: FormData.fromMap( - { - "code": controller.controller.value.text, - }, ), - options: Options( - headers: { - "X-Login-Token": - await storage.read(key: "token") - }, - ), - ); - controller.getRegistrationCodes(); - }, - ), - ]), - ]), - ), - actions: [ - TextButton( - onPressed: () => Get.back(), - child: const Text('OK'), + ], + ); + } + return const SizedBox(); + }, ), - ], - ), - child: const Text("Administratorska plošča"), - ), - ), - const SizedBox( - height: 10, - ), - Center( - child: ElevatedButton( - onPressed: () => Get.defaultDialog( - title: 'Posnetek igre', - content: SingleChildScrollView( - child: Obx( - () => Column(children: [ - const Text( - 'Tukaj lahko vpišete URL do posnetka igre', - ), - const SizedBox( - height: 10, - ), - Row(children: [ - Expanded( - child: TextField( - controller: controller.replayController.value, - decoration: const InputDecoration( - border: UnderlineInputBorder(), - labelText: 'Povezava do posnetka igre', - ), + Row(children: [ + Expanded( + child: TextField( + controller: controller.controller.value, + decoration: const InputDecoration( + border: UnderlineInputBorder(), + labelText: 'Nova registracijska koda', ), ), - ]), + ), + IconButton( + icon: const Icon(Icons.save), + onPressed: () async { + await dio.post( + "$BACKEND_URL/admin/reg_code", + data: FormData.fromMap( + { + "code": controller.controller.value.text, + }, + ), + options: Options( + headers: { + "X-Login-Token": + await storage.read(key: "token") + }, + ), + ); + controller.getRegistrationCodes(); + }, + ), ]), - ), + ]), ), actions: [ TextButton( - onPressed: () { - Get.back(); - }, - child: const Text('Prekliči'), - ), - TextButton( - onPressed: () { - joinReplay(controller.replayController.value.text); - }, + onPressed: () => Get.back(), child: const Text('OK'), ), ], ), - child: const Text("Posnetek igre"), + child: const Text("Administratorska plošča"), ), ), - - const SizedBox( - height: 10, + const SizedBox( + height: 10, + ), + Center( + child: ElevatedButton( + onPressed: () => Get.defaultDialog( + title: 'Posnetek igre', + content: SingleChildScrollView( + child: Obx( + () => Column(children: [ + const Text( + 'Tukaj lahko vpišete URL do posnetka igre', + ), + const SizedBox( + height: 10, + ), + Row(children: [ + Expanded( + child: TextField( + controller: controller.replayController.value, + decoration: const InputDecoration( + border: UnderlineInputBorder(), + labelText: 'Povezava do posnetka igre', + ), + ), + ), + ]), + ]), + ), + ), + actions: [ + TextButton( + onPressed: () { + Get.back(); + }, + child: const Text('Prekliči'), + ), + TextButton( + onPressed: () { + joinReplay(controller.replayController.value.text); + }, + child: const Text('OK'), + ), + ], + ), + child: const Text("Posnetek igre"), ), - Center( - child: ElevatedButton( - onPressed: () => showDialog( - context: context, - builder: (context) { - return StatefulBuilder(builder: (context, setState) { - return AlertDialog( - title: const Text('Prilagodi računalniške igralce'), - content: SingleChildScrollView( - child: Obx( - () => Column(children: [ - const Text( - 'Tukaj lahko urejate, kakšne bote želite videti v svojih igrah. Program bo ob vstopu v igro avtomatično izbral naključne igralce iz tega seznama, če jih je vsaj toliko, kot zahteva ta igra.', - ), - const SizedBox( - height: 10, - ), - FutureBuilder( - future: controller.getBots(), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (controller.botNames.isNotEmpty) { - return DataTable( - columns: const [ - DataColumn( - label: Expanded( - child: Text( - 'Bot', - style: TextStyle( - fontStyle: - FontStyle.italic), - ), + ), + + const SizedBox( + height: 10, + ), + Center( + child: ElevatedButton( + onPressed: () => showDialog( + context: context, + builder: (context) { + return StatefulBuilder(builder: (context, setState) { + return AlertDialog( + title: const Text('Prilagodi računalniške igralce'), + content: SingleChildScrollView( + child: Obx( + () => Column(children: [ + const Text( + 'Tukaj lahko urejate, kakšne bote želite videti v svojih igrah. Program bo ob vstopu v igro avtomatično izbral naključne igralce iz tega seznama, če jih je vsaj toliko, kot zahteva ta igra.', + ), + const SizedBox( + height: 10, + ), + FutureBuilder( + future: controller.getBots(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (controller.botNames.isNotEmpty) { + return DataTable( + columns: const [ + DataColumn( + label: Expanded( + child: Text( + 'Bot', + style: TextStyle( + fontStyle: + FontStyle.italic), ), ), - DataColumn( - label: Expanded( - child: Text( - 'Ime', - style: TextStyle( - fontStyle: - FontStyle.italic), - ), + ), + DataColumn( + label: Expanded( + child: Text( + 'Ime', + style: TextStyle( + fontStyle: + FontStyle.italic), ), ), - DataColumn( - label: Expanded( - child: Text( - 'Izbriši', - style: TextStyle( - fontStyle: - FontStyle.italic), - ), + ), + DataColumn( + label: Expanded( + child: Text( + 'Izbriši', + style: TextStyle( + fontStyle: + FontStyle.italic), ), ), - ], - rows: [ - ...controller.botNames.map( - (e) => DataRow( - cells: [ - DataCell(Text(e["type"])), - DataCell(Text(e["name"])), - DataCell( - IconButton( - icon: const Icon( - Icons.delete), - onPressed: () async { - await controller - .deleteBot( - e["name"], - e["type"], - ); - await controller - .getBots(); - setState(() {}); - }, - ), + ), + ], + rows: [ + ...controller.botNames.map( + (e) => DataRow( + cells: [ + DataCell(Text(e["type"])), + DataCell(Text(e["name"])), + DataCell( + IconButton( + icon: const Icon( + Icons.delete), + onPressed: () async { + await controller + .deleteBot( + e["name"], + e["type"], + ); + await controller + .getBots(); + setState(() {}); + }, ), - ], - ), + ), + ], ), - ], - ); - } - return const SizedBox(); - }, - ), - Row(children: [ - Expanded( - child: TextField( - controller: controller - .playerNameController.value, - decoration: const InputDecoration( - border: UnderlineInputBorder(), - labelText: 'Ime igralca', - ), - ), - ), - IconButton( - icon: const Icon(Icons.replay_outlined), - onPressed: () async { - String botName = - controller.randomBotName(); - controller.playerNameController.value - .text = botName; - setState(() {}); - }, - ), - ]), - const SizedBox( - height: 10, - ), - Center( - child: SegmentedButton( - segments: >[ - ...BOTS.map( - (e) => ButtonSegment( - value: e, - label: Text(e["name"].toString()), ), - ) - ], - selected: {controller.dropdownValue}, - onSelectionChanged: - (Set newSelection) { - controller.dropdownValue = - newSelection.first; - setState(() {}); - }, + ], + ); + } + return const SizedBox(); + }, + ), + Row(children: [ + Expanded( + child: TextField( + controller: + controller.playerNameController.value, + decoration: const InputDecoration( + border: UnderlineInputBorder(), + labelText: 'Ime igralca', + ), ), ), - const SizedBox( - height: 10, - ), - ElevatedButton.icon( + IconButton( + icon: const Icon(Icons.replay_outlined), onPressed: () async { - await controller.newBot( - controller - .playerNameController.value.text, - controller.dropdownValue["type"] - as String, - ); + String botName = + controller.randomBotName(); + controller.playerNameController.value + .text = botName; setState(() {}); }, - label: const Text( - "Dodaj bota na seznam", - style: TextStyle( - fontSize: 20, - ), - ), - icon: const Icon(Icons.add), ), ]), - ), + const SizedBox( + height: 10, + ), + Center( + child: SegmentedButton( + segments: >[ + ...BOTS.map( + (e) => ButtonSegment( + value: e, + label: Text(e["name"].toString()), + ), + ) + ], + selected: {controller.dropdownValue}, + onSelectionChanged: + (Set newSelection) { + controller.dropdownValue = + newSelection.first; + setState(() {}); + }, + ), + ), + const SizedBox( + height: 10, + ), + ElevatedButton.icon( + onPressed: () async { + await controller.newBot( + controller + .playerNameController.value.text, + controller.dropdownValue["type"] + as String, + ); + setState(() {}); + }, + label: const Text( + "Dodaj bota na seznam", + style: TextStyle( + fontSize: 20, + ), + ), + icon: const Icon(Icons.add), + ), + ]), ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('Končaj z urejanjem'), - ), - ], - ); - }); - }), - child: const Text("Prilagodi računalniške igralce"), - ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Končaj z urejanjem'), + ), + ], + ); + }); + }), + child: const Text("Prilagodi računalniške igralce"), ), + ), - // PRIORITY QUEUE - GridView.count( - childAspectRatio: 0.75, - crossAxisCount: 4, - physics: - const NeverScrollableScrollPhysics(), // to disable GridView's scrolling - shrinkWrap: true, - children: [ - ...controller.priorityQueue.map( - (e) => GestureDetector( - onTap: () { - debugPrint("Priority"); - Get.toNamed("/game", parameters: { - "playing": e.requiredPlayers.toString(), - "gameId": e.id, - "bots": "false", - }); - }, - child: Card( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: Text( - 'Igra ${e.totalTime}+${e.additionalTime} ${e.mondfangRadelci || e.skisfang || e.napovedanMondfang ? "+ modifikacije" : ""} ${e.type == "klepetalnica" ? "(klepetalnica)" : ""}'), + // PRIORITY QUEUE + GridView.count( + childAspectRatio: 0.75, + crossAxisCount: 4, + physics: + const NeverScrollableScrollPhysics(), // to disable GridView's scrolling + shrinkWrap: true, + children: [ + ...controller.priorityQueue.map( + (e) => GestureDetector( + onTap: () { + debugPrint("Priority"); + Get.toNamed("/game", parameters: { + "playing": e.requiredPlayers.toString(), + "gameId": e.id, + "bots": "false", + }); + }, + child: Card( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Text( + 'Igra ${e.totalTime}+${e.additionalTime} ${e.mondfangRadelci || e.skisfang || e.napovedanMondfang ? "+ modifikacije" : ""} ${e.type == "klepetalnica" ? "(klepetalnica)" : ""}'), + ), + if (e.mondfangRadelci) + const Center( + child: Text('Mondfang radelci'), ), - if (e.mondfangRadelci) - const Center( - child: Text('Mondfang radelci'), - ), - if (e.skisfang) - const Center( - child: Text('Škisfang'), - ), - if (e.napovedanMondfang) - const Center( - child: Text('Napovedan mondfang'), - ), - const SizedBox( - height: 10, + if (e.skisfang) + const Center( + child: Text('Škisfang'), ), - ...e.user.map( - (k) => SizedBox( - height: 40, - child: Text( - k.name, - style: const TextStyle( - fontSize: 25, - ), + if (e.napovedanMondfang) + const Center( + child: Text('Napovedan mondfang'), + ), + const SizedBox( + height: 10, + ), + ...e.user.map( + (k) => SizedBox( + height: 40, + child: Text( + k.name, + style: const TextStyle( + fontSize: 25, ), ), ), - ListView.builder( - shrinkWrap: true, - itemCount: e.requiredPlayers - e.user.length, - itemBuilder: (BuildContext context, int index) { - return const Center( - child: SizedBox( - height: 40, - child: Text( - "Pridružite se igri", - style: TextStyle( - fontSize: 25, - ), + ), + ListView.builder( + shrinkWrap: true, + itemCount: e.requiredPlayers - e.user.length, + itemBuilder: (BuildContext context, int index) { + return const Center( + child: SizedBox( + height: 40, + child: Text( + "Pridružite se igri", + style: TextStyle( + fontSize: 25, ), ), - ); - }, - ), - ], - ), + ), + ); + }, + ), + ], ), ), ), - ], - ), + ), + ], + ), - // QUEUE - GridView.count( - crossAxisCount: 4, - physics: - const NeverScrollableScrollPhysics(), // to disable GridView's scrolling - shrinkWrap: true, - children: [ - ...controller.queue.map( - (e) => GestureDetector( - onTap: () { - Get.toNamed("/game", parameters: { - "playing": e.requiredPlayers.toString(), - "gameId": e.id, - "bots": "false", - }); - }, - child: Card( - child: Column( - children: [ - Center( - child: Text( - 'Igra ${e.totalTime}+${e.additionalTime} ${e.mondfangRadelci || e.skisfang || e.napovedanMondfang ? "+ modifikacije" : ""} ${e.type == "klepetalnica" ? "(klepetalnica)" : ""}'), + // QUEUE + GridView.count( + crossAxisCount: 4, + physics: + const NeverScrollableScrollPhysics(), // to disable GridView's scrolling + shrinkWrap: true, + children: [ + ...controller.queue.map( + (e) => GestureDetector( + onTap: () { + Get.toNamed("/game", parameters: { + "playing": e.requiredPlayers.toString(), + "gameId": e.id, + "bots": "false", + }); + }, + child: Card( + child: Column( + children: [ + Center( + child: Text( + 'Igra ${e.totalTime}+${e.additionalTime} ${e.mondfangRadelci || e.skisfang || e.napovedanMondfang ? "+ modifikacije" : ""} ${e.type == "klepetalnica" ? "(klepetalnica)" : ""}'), + ), + if (e.mondfangRadelci) + const Center( + child: Text('Mondfang radelci'), ), - if (e.mondfangRadelci) - const Center( - child: Text('Mondfang radelci'), - ), - if (e.skisfang) - const Center( - child: Text('Škisfang'), - ), - if (e.napovedanMondfang) - const Center( - child: Text('Napovedan mondfang'), - ), - const SizedBox( - height: 10, + if (e.skisfang) + const Center( + child: Text('Škisfang'), ), - ...e.user.map( - (k) => SizedBox( - height: 40, - child: Text( - k.name, - style: const TextStyle( - fontSize: 25, - ), + if (e.napovedanMondfang) + const Center( + child: Text('Napovedan mondfang'), + ), + const SizedBox( + height: 10, + ), + ...e.user.map( + (k) => SizedBox( + height: 40, + child: Text( + k.name, + style: const TextStyle( + fontSize: 25, ), ), ), - ...List.generate( - e.requiredPlayers - e.user.length, - (index) => const SizedBox( - height: 40, - child: Text( - "Pridružite se igri", - style: TextStyle( - fontSize: 25, - ), + ), + ...List.generate( + e.requiredPlayers - e.user.length, + (index) => const SizedBox( + height: 40, + child: Text( + "Pridružite se igri", + style: TextStyle( + fontSize: 25, ), ), ), - ], - ), + ), + ], ), ), ), - ], - ), - ], - ), + ), + ], + ), + ], ), ), - floatingActionButton: FloatingActionButton( - onPressed: controller.dialog, - tooltip: 'Ustvari novo igro', - child: const Icon(Icons.add), - ), ); } } diff --git a/tarok/lib/lobby_controller.dart b/tarok/lib/lobby_controller.dart index 68ea869..4b05ef1 100644 --- a/tarok/lib/lobby_controller.dart +++ b/tarok/lib/lobby_controller.dart @@ -68,6 +68,18 @@ class Friend { String relationshipId; } +class Replay { + Replay({ + required this.url, + required this.gameId, + required this.createdAt, + }); + + String url; + String gameId; + String createdAt; +} + class LobbyController extends GetxController { var priorityQueue = [].obs; var queue = [].obs; @@ -92,6 +104,7 @@ class LobbyController extends GetxController { var prihodne = [].obs; var prijatelji = [].obs; var emailController = TextEditingController().obs; + var replays = [].obs; Map dropdownValue = BOTS.first; @@ -687,6 +700,14 @@ class LobbyController extends GetxController { odhodne.refresh(); break; } + } else if (msg.hasReplay()) { + final replay = msg.replay; + replays.add(Replay( + url: replay.url, + gameId: replay.gameId, + createdAt: replay.createdAt, + )); + replays.refresh(); } }, onDone: () { diff --git a/tarok/lib/main.dart b/tarok/lib/main.dart index b490b35..2481a44 100644 --- a/tarok/lib/main.dart +++ b/tarok/lib/main.dart @@ -13,6 +13,7 @@ import 'package:tarok/lobby.dart'; import 'package:tarok/login.dart'; import 'package:tarok/register.dart'; import 'package:tarok/replay.dart'; +import 'package:tarok/replays.dart'; import 'package:tarok/settings.dart'; import 'package:tarok/sounds.dart'; import 'package:url_strategy/url_strategy.dart'; @@ -54,6 +55,12 @@ void main() async { ]); SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + if (THEME == "light") { + Get.changeThemeMode(ThemeMode.light); + } else { + Get.changeThemeMode(ThemeMode.dark); + } + runApp( GetMaterialApp( title: 'Tarok palcka.si', @@ -71,6 +78,7 @@ void main() async { GetPage(name: '/registration', page: () => const Register()), GetPage(name: '/friends', page: () => const Friends()), GetPage(name: '/about', page: () => const About()), + GetPage(name: '/replays', page: () => const Replays()), GetPage( name: '/replay/:id', page: () => FutureBuilder( @@ -87,7 +95,6 @@ void main() async { useMaterial3: true, brightness: Brightness.dark, ), - themeMode: THEME == "light" ? ThemeMode.light : ThemeMode.dark, builder: InAppNotifications.init(), home: const Lobby(), ), diff --git a/tarok/lib/messages/lobby_messages.pb.dart b/tarok/lib/messages/lobby_messages.pb.dart index dda154e..35cbf77 100644 --- a/tarok/lib/messages/lobby_messages.pb.dart +++ b/tarok/lib/messages/lobby_messages.pb.dart @@ -1231,6 +1231,81 @@ class RemoveFriend extends $pb.GeneratedMessage { void clearRelationshipId() => clearField(1); } +class Replay extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Replay', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'lobby_messages'), createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'url') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gameId', protoName: 'gameId') + ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createdAt', protoName: 'createdAt') + ..hasRequiredFields = false + ; + + Replay._() : super(); + factory Replay({ + $core.String? url, + $core.String? gameId, + $core.String? createdAt, + }) { + final _result = create(); + if (url != null) { + _result.url = url; + } + if (gameId != null) { + _result.gameId = gameId; + } + if (createdAt != null) { + _result.createdAt = createdAt; + } + return _result; + } + factory Replay.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Replay.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + Replay clone() => Replay()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Replay copyWith(void Function(Replay) updates) => super.copyWith((message) => updates(message as Replay)) as Replay; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static Replay create() => Replay._(); + Replay createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static Replay getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Replay? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get url => $_getSZ(0); + @$pb.TagNumber(1) + set url($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasUrl() => $_has(0); + @$pb.TagNumber(1) + void clearUrl() => clearField(1); + + @$pb.TagNumber(2) + $core.String get gameId => $_getSZ(1); + @$pb.TagNumber(2) + set gameId($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasGameId() => $_has(1); + @$pb.TagNumber(2) + void clearGameId() => clearField(2); + + @$pb.TagNumber(3) + $core.String get createdAt => $_getSZ(2); + @$pb.TagNumber(3) + set createdAt($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasCreatedAt() => $_has(2); + @$pb.TagNumber(3) + void clearCreatedAt() => clearField(3); +} + enum LobbyMessage_Data { loginRequest, loginInfo, @@ -1246,6 +1321,7 @@ enum LobbyMessage_Data { friendRequestAcceptDecline, friendRequestSend, removeFriend, + replay, notSet } @@ -1265,10 +1341,11 @@ class LobbyMessage extends $pb.GeneratedMessage { 22 : LobbyMessage_Data.friendRequestAcceptDecline, 23 : LobbyMessage_Data.friendRequestSend, 24 : LobbyMessage_Data.removeFriend, + 25 : LobbyMessage_Data.replay, 0 : LobbyMessage_Data.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'LobbyMessage', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'lobby_messages'), createEmptyInstance: create) - ..oo(0, [10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24]) + ..oo(0, [10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25]) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'playerId') ..aOM(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'loginRequest', subBuilder: LoginRequest.create) ..aOM(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'loginInfo', subBuilder: LoginInfo.create) @@ -1284,6 +1361,7 @@ class LobbyMessage extends $pb.GeneratedMessage { ..aOM(22, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'friendRequestAcceptDecline', subBuilder: FriendRequestAcceptDecline.create) ..aOM(23, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'friendRequestSend', subBuilder: FriendRequestSend.create) ..aOM(24, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'removeFriend', subBuilder: RemoveFriend.create) + ..aOM(25, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'replay', subBuilder: Replay.create) ..hasRequiredFields = false ; @@ -1304,6 +1382,7 @@ class LobbyMessage extends $pb.GeneratedMessage { FriendRequestAcceptDecline? friendRequestAcceptDecline, FriendRequestSend? friendRequestSend, RemoveFriend? removeFriend, + Replay? replay, }) { final _result = create(); if (playerId != null) { @@ -1351,6 +1430,9 @@ class LobbyMessage extends $pb.GeneratedMessage { if (removeFriend != null) { _result.removeFriend = removeFriend; } + if (replay != null) { + _result.replay = replay; + } return _result; } factory LobbyMessage.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); @@ -1539,5 +1621,16 @@ class LobbyMessage extends $pb.GeneratedMessage { void clearRemoveFriend() => clearField(24); @$pb.TagNumber(24) RemoveFriend ensureRemoveFriend() => $_ensure(14); + + @$pb.TagNumber(25) + Replay get replay => $_getN(15); + @$pb.TagNumber(25) + set replay(Replay v) { setField(25, v); } + @$pb.TagNumber(25) + $core.bool hasReplay() => $_has(15); + @$pb.TagNumber(25) + void clearReplay() => clearField(25); + @$pb.TagNumber(25) + Replay ensureReplay() => $_ensure(15); } diff --git a/tarok/lib/messages/lobby_messages.pbjson.dart b/tarok/lib/messages/lobby_messages.pbjson.dart index dd08322..41a8cf4 100644 --- a/tarok/lib/messages/lobby_messages.pbjson.dart +++ b/tarok/lib/messages/lobby_messages.pbjson.dart @@ -213,6 +213,18 @@ const RemoveFriend$json = const { /// Descriptor for `RemoveFriend`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List removeFriendDescriptor = $convert.base64Decode('CgxSZW1vdmVGcmllbmQSJgoOcmVsYXRpb25zaGlwSWQYASABKAlSDnJlbGF0aW9uc2hpcElk'); +@$core.Deprecated('Use replayDescriptor instead') +const Replay$json = const { + '1': 'Replay', + '2': const [ + const {'1': 'url', '3': 1, '4': 1, '5': 9, '10': 'url'}, + const {'1': 'gameId', '3': 2, '4': 1, '5': 9, '10': 'gameId'}, + const {'1': 'createdAt', '3': 3, '4': 1, '5': 9, '10': 'createdAt'}, + ], +}; + +/// Descriptor for `Replay`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List replayDescriptor = $convert.base64Decode('CgZSZXBsYXkSEAoDdXJsGAEgASgJUgN1cmwSFgoGZ2FtZUlkGAIgASgJUgZnYW1lSWQSHAoJY3JlYXRlZEF0GAMgASgJUgljcmVhdGVkQXQ='); @$core.Deprecated('Use lobbyMessageDescriptor instead') const LobbyMessage$json = const { '1': 'LobbyMessage', @@ -232,6 +244,7 @@ const LobbyMessage$json = const { const {'1': 'friend_request_accept_decline', '3': 22, '4': 1, '5': 11, '6': '.lobby_messages.FriendRequestAcceptDecline', '9': 0, '10': 'friendRequestAcceptDecline'}, const {'1': 'friend_request_send', '3': 23, '4': 1, '5': 11, '6': '.lobby_messages.FriendRequestSend', '9': 0, '10': 'friendRequestSend'}, const {'1': 'remove_friend', '3': 24, '4': 1, '5': 11, '6': '.lobby_messages.RemoveFriend', '9': 0, '10': 'removeFriend'}, + const {'1': 'replay', '3': 25, '4': 1, '5': 11, '6': '.lobby_messages.Replay', '9': 0, '10': 'replay'}, ], '8': const [ const {'1': 'data'}, @@ -239,4 +252,4 @@ const LobbyMessage$json = const { }; /// Descriptor for `LobbyMessage`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List lobbyMessageDescriptor = $convert.base64Decode('CgxMb2JieU1lc3NhZ2USGwoJcGxheWVyX2lkGAEgASgJUghwbGF5ZXJJZBJDCg1sb2dpbl9yZXF1ZXN0GAogASgLMhwubG9iYnlfbWVzc2FnZXMuTG9naW5SZXF1ZXN0SABSDGxvZ2luUmVxdWVzdBI6Cgpsb2dpbl9pbmZvGAsgASgLMhkubG9iYnlfbWVzc2FnZXMuTG9naW5JbmZvSABSCWxvZ2luSW5mbxJGCg5sb2dpbl9yZXNwb25zZRgMIAEoCzIdLmxvYmJ5X21lc3NhZ2VzLkxvZ2luUmVzcG9uc2VIAFINbG9naW5SZXNwb25zZRJACgxnYW1lX2NyZWF0ZWQYDSABKAsyGy5sb2JieV9tZXNzYWdlcy5HYW1lQ3JlYXRlZEgAUgtnYW1lQ3JlYXRlZBJGCg5nYW1lX2Rpc2JhbmRlZBgOIAEoCzIdLmxvYmJ5X21lc3NhZ2VzLkdhbWVEaXNiYW5kZWRIAFINZ2FtZURpc2JhbmRlZBI3CglnYW1lX2pvaW4YDyABKAsyGC5sb2JieV9tZXNzYWdlcy5HYW1lSm9pbkgAUghnYW1lSm9pbhI6CgpnYW1lX2xlYXZlGBAgASgLMhkubG9iYnlfbWVzc2FnZXMuR2FtZUxlYXZlSABSCWdhbWVMZWF2ZRI3CglnYW1lX21vdmUYESABKAsyGC5sb2JieV9tZXNzYWdlcy5HYW1lTW92ZUgAUghnYW1lTW92ZRI9CgtnYW1lX2ludml0ZRgSIAEoCzIaLmxvYmJ5X21lc3NhZ2VzLkdhbWVJbnZpdGVIAFIKZ2FtZUludml0ZRJWChRmcmllbmRfb25saW5lX3N0YXR1cxgUIAEoCzIiLmxvYmJ5X21lc3NhZ2VzLkZyaWVuZE9ubGluZVN0YXR1c0gAUhJmcmllbmRPbmxpbmVTdGF0dXMSMAoGZnJpZW5kGBUgASgLMhYubG9iYnlfbWVzc2FnZXMuRnJpZW5kSABSBmZyaWVuZBJvCh1mcmllbmRfcmVxdWVzdF9hY2NlcHRfZGVjbGluZRgWIAEoCzIqLmxvYmJ5X21lc3NhZ2VzLkZyaWVuZFJlcXVlc3RBY2NlcHREZWNsaW5lSABSGmZyaWVuZFJlcXVlc3RBY2NlcHREZWNsaW5lElMKE2ZyaWVuZF9yZXF1ZXN0X3NlbmQYFyABKAsyIS5sb2JieV9tZXNzYWdlcy5GcmllbmRSZXF1ZXN0U2VuZEgAUhFmcmllbmRSZXF1ZXN0U2VuZBJDCg1yZW1vdmVfZnJpZW5kGBggASgLMhwubG9iYnlfbWVzc2FnZXMuUmVtb3ZlRnJpZW5kSABSDHJlbW92ZUZyaWVuZEIGCgRkYXRh'); +final $typed_data.Uint8List lobbyMessageDescriptor = $convert.base64Decode('CgxMb2JieU1lc3NhZ2USGwoJcGxheWVyX2lkGAEgASgJUghwbGF5ZXJJZBJDCg1sb2dpbl9yZXF1ZXN0GAogASgLMhwubG9iYnlfbWVzc2FnZXMuTG9naW5SZXF1ZXN0SABSDGxvZ2luUmVxdWVzdBI6Cgpsb2dpbl9pbmZvGAsgASgLMhkubG9iYnlfbWVzc2FnZXMuTG9naW5JbmZvSABSCWxvZ2luSW5mbxJGCg5sb2dpbl9yZXNwb25zZRgMIAEoCzIdLmxvYmJ5X21lc3NhZ2VzLkxvZ2luUmVzcG9uc2VIAFINbG9naW5SZXNwb25zZRJACgxnYW1lX2NyZWF0ZWQYDSABKAsyGy5sb2JieV9tZXNzYWdlcy5HYW1lQ3JlYXRlZEgAUgtnYW1lQ3JlYXRlZBJGCg5nYW1lX2Rpc2JhbmRlZBgOIAEoCzIdLmxvYmJ5X21lc3NhZ2VzLkdhbWVEaXNiYW5kZWRIAFINZ2FtZURpc2JhbmRlZBI3CglnYW1lX2pvaW4YDyABKAsyGC5sb2JieV9tZXNzYWdlcy5HYW1lSm9pbkgAUghnYW1lSm9pbhI6CgpnYW1lX2xlYXZlGBAgASgLMhkubG9iYnlfbWVzc2FnZXMuR2FtZUxlYXZlSABSCWdhbWVMZWF2ZRI3CglnYW1lX21vdmUYESABKAsyGC5sb2JieV9tZXNzYWdlcy5HYW1lTW92ZUgAUghnYW1lTW92ZRI9CgtnYW1lX2ludml0ZRgSIAEoCzIaLmxvYmJ5X21lc3NhZ2VzLkdhbWVJbnZpdGVIAFIKZ2FtZUludml0ZRJWChRmcmllbmRfb25saW5lX3N0YXR1cxgUIAEoCzIiLmxvYmJ5X21lc3NhZ2VzLkZyaWVuZE9ubGluZVN0YXR1c0gAUhJmcmllbmRPbmxpbmVTdGF0dXMSMAoGZnJpZW5kGBUgASgLMhYubG9iYnlfbWVzc2FnZXMuRnJpZW5kSABSBmZyaWVuZBJvCh1mcmllbmRfcmVxdWVzdF9hY2NlcHRfZGVjbGluZRgWIAEoCzIqLmxvYmJ5X21lc3NhZ2VzLkZyaWVuZFJlcXVlc3RBY2NlcHREZWNsaW5lSABSGmZyaWVuZFJlcXVlc3RBY2NlcHREZWNsaW5lElMKE2ZyaWVuZF9yZXF1ZXN0X3NlbmQYFyABKAsyIS5sb2JieV9tZXNzYWdlcy5GcmllbmRSZXF1ZXN0U2VuZEgAUhFmcmllbmRSZXF1ZXN0U2VuZBJDCg1yZW1vdmVfZnJpZW5kGBggASgLMhwubG9iYnlfbWVzc2FnZXMuUmVtb3ZlRnJpZW5kSABSDHJlbW92ZUZyaWVuZBIwCgZyZXBsYXkYGSABKAsyFi5sb2JieV9tZXNzYWdlcy5SZXBsYXlIAFIGcmVwbGF5QgYKBGRhdGE='); diff --git a/tarok/lib/replays.dart b/tarok/lib/replays.dart new file mode 100644 index 0000000..a934979 --- /dev/null +++ b/tarok/lib/replays.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart' hide FormData; +import 'package:tarok/lobby_controller.dart'; +import 'package:tarok/replay.dart'; +import 'package:tarok/ui/main_page.dart'; + +class Replays extends StatelessWidget { + const Replays({super.key}); + + @override + Widget build(BuildContext context) { + LobbyController controller = Get.put(LobbyController()); + + return PalckaHome( + child: Obx( + () => ListView( + padding: + const EdgeInsets.only(left: 50, right: 50, top: 10, bottom: 10), + children: [ + const Center( + child: Text( + 'Posnetki iger', + style: TextStyle(fontSize: 40), + ), + ), + ...controller.replays.map( + (e) => Row( + children: [ + Text(e.gameId), + const Spacer(), + Text(e.createdAt), + const Spacer(), + ElevatedButton( + onPressed: () async { + await joinReplay(e.url); + }, + child: const Text("Oglej si posnetek"), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/tarok/lib/settings.dart b/tarok/lib/settings.dart index 7e08862..007a816 100644 --- a/tarok/lib/settings.dart +++ b/tarok/lib/settings.dart @@ -39,11 +39,10 @@ class _SettingsState extends State { await prefs.setString("theme", value ? "dark" : "light"); THEME = prefs.getString("theme") ?? "dark"; if (THEME == "light") { - Get.changeTheme(ThemeData.light()); + Get.changeThemeMode(ThemeMode.light); } else { - Get.changeTheme(ThemeData.dark()); + Get.changeThemeMode(ThemeMode.dark); } - setState(() {}); }, initialValue: THEME == "dark", leading: const Icon(Icons.dark_mode), diff --git a/tarok/lib/ui/main_page.dart b/tarok/lib/ui/main_page.dart new file mode 100644 index 0000000..d853add --- /dev/null +++ b/tarok/lib/ui/main_page.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tarok/constants.dart'; + +class PalckaHome extends StatelessWidget { + const PalckaHome({ + super.key, + required this.child, + this.floatingActionButton, + }); + + final Widget child; + final Widget? floatingActionButton; + + @override + Widget build(BuildContext context) { + return Scaffold( + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + ), + child: const Text( + 'Palčka.si tarok program', + style: TextStyle( + color: Colors.white, + fontSize: 24, + ), + ), + ), + ListTile( + leading: const Icon(Icons.home), + title: const Text('Domov'), + onTap: () { + Get.toNamed("/"); + }, + ), + ListTile( + leading: const Icon(Icons.people), + title: const Text('Prijatelji'), + onTap: () { + Get.toNamed("/friends"); + }, + ), + ListTile( + leading: const Icon(Icons.replay), + title: const Text('Posnetki iger'), + onTap: () { + Get.toNamed("/replays"); + }, + ), + ], + ), + ), + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text("Palčka"), + actions: [ + IconButton( + icon: const Icon(Icons.settings), + onPressed: () async { + Get.toNamed("/settings"); + }, + ), + IconButton( + icon: const Icon(Icons.info), + onPressed: () async { + Get.toNamed("/about"); + }, + ), + IconButton( + icon: const Icon(Icons.logout), + onPressed: () async { + await storage.deleteAll(); + Get.toNamed("/login"); + }, + ), + ], + ), + body: Center(child: child), + floatingActionButton: floatingActionButton, + ); + } +}