diff --git a/DM/Services/DM.Services.DataAccess/BusinessObjects/Users/Token.cs b/DM/Services/DM.Services.DataAccess/BusinessObjects/Users/Token.cs index d3cc85cc..d4ffceea 100644 --- a/DM/Services/DM.Services.DataAccess/BusinessObjects/Users/Token.cs +++ b/DM/Services/DM.Services.DataAccess/BusinessObjects/Users/Token.cs @@ -9,6 +9,7 @@ namespace DM.Services.DataAccess.BusinessObjects.Users; /// /// DAL model for authorization token /// +[Table("Tokens")] public class Token : IRemovable { /// diff --git a/DM/Services/DM.Services.Gaming/BusinessProcesses/Games/Reading/GameReadingService.cs b/DM/Services/DM.Services.Gaming/BusinessProcesses/Games/Reading/GameReadingService.cs index 28e9dc60..b3b54886 100644 --- a/DM/Services/DM.Services.Gaming/BusinessProcesses/Games/Reading/GameReadingService.cs +++ b/DM/Services/DM.Services.Gaming/BusinessProcesses/Games/Reading/GameReadingService.cs @@ -88,7 +88,7 @@ await unreadCountersRepository.FillEntityCounters( var gameRoomIds = roomIds.ToArray(); game.UnreadPostsCount = gameRoomIds.Sum(id => unreadPostCounters.TryGetValue(id, out var count) ? count : 0); - game.Pendings = pendingPosts.Where(p => gameRoomIds.Contains(p.RoomId)); + game.PendingPosts = pendingPosts.Where(p => gameRoomIds.Contains(p.RoomId)); } return games; diff --git a/DM/Services/DM.Services.Gaming/Dto/Output/Game.cs b/DM/Services/DM.Services.Gaming/Dto/Output/Game.cs index df2be661..9f72deef 100644 --- a/DM/Services/DM.Services.Gaming/Dto/Output/Game.cs +++ b/DM/Services/DM.Services.Gaming/Dto/Output/Game.cs @@ -57,7 +57,6 @@ public class Game /// /// Pending assistant if any - /// Due to some issues in EFCore 3.1.5 had to move to IEnumerable, but make no mistake - that is a single assistant every time! /// public GeneralUser PendingAssistant { get; set; } @@ -79,7 +78,7 @@ public class Game /// /// Game pending posts /// - public IEnumerable Pendings { get; set; } + public IEnumerable PendingPosts { get; set; } /// /// Game title diff --git a/DM/Web/DM.Web.API/Startup.cs b/DM/Web/DM.Web.API/Startup.cs index 67b61313..bc33da73 100644 --- a/DM/Web/DM.Web.API/Startup.cs +++ b/DM/Web/DM.Web.API/Startup.cs @@ -154,7 +154,7 @@ public void Configure(IApplicationBuilder appBuilder, if (migrateOnStart) { dbContext.Database.Migrate(); - throw new Exception("Migration is complete"); + Environment.Exit(-1); } appBuilder diff --git a/DM/Web/DM.Web.Modern/src/api/models/gaming/index.ts b/DM/Web/DM.Web.Modern/src/api/models/gaming/index.ts index f9078296..0c86e27c 100644 --- a/DM/Web/DM.Web.Modern/src/api/models/gaming/index.ts +++ b/DM/Web/DM.Web.Modern/src/api/models/gaming/index.ts @@ -1,4 +1,15 @@ +import { User } from '@/api/models/community'; + export * from './games'; export * from './attributes'; export * from './characters'; export * from './rooms'; + +export interface Comment { + id: string; + author: User; + created: string; + updated: string | null; + text: string; + likes: User[]; +} diff --git a/DM/Web/DM.Web.Modern/src/api/requests/gamingApi.ts b/DM/Web/DM.Web.Modern/src/api/requests/gamingApi.ts index 4e142b84..d6a9891e 100644 --- a/DM/Web/DM.Web.Modern/src/api/requests/gamingApi.ts +++ b/DM/Web/DM.Web.Modern/src/api/requests/gamingApi.ts @@ -1,5 +1,5 @@ -import { ApiResult, Envelope, ListEnvelope } from '@/api/models/common'; -import { Game, AttributeSchema, Tag, Character, Room } from '@/api/models/gaming'; +import { ApiResult, Envelope, ListEnvelope, PagingQuery } from '@/api/models/common'; +import { Game, AttributeSchema, Tag, Character, Room, Comment } from '@/api/models/gaming'; import { User } from '@/api/models/community'; import Api from '@/api'; @@ -33,6 +33,11 @@ export default new class { return data!; } + public async getComments(gameId: string, query: PagingQuery): Promise> { + const { data } = await Api.get>(`games/${gameId}/comments`, query); + return data!; + } + public async getSchemas(): Promise> { const { data } = await Api.get>('schemata'); return data!; @@ -51,15 +56,27 @@ export default new class { return await Api.post>('games', game); } - public async createCharacter(id: string, character: Character): Promise>> { - return await Api.post>(`games/${id}/characters`, character); + public async createCharacter(gameId: string, character: Character): Promise>> { + return await Api.post>(`games/${gameId}/characters`, character); + } + + public async createComment(gameId: string, comment: Comment): Promise>> { + return await Api.post>(`games/${gameId}/comments`, comment); + } + + public async subscribe(gameId: string): Promise>> { + return await Api.post>(`games/${gameId}/readers`); + } + + public async unsubscribe(gameId: string): Promise> { + return await Api.delete(`games/${gameId}/readers`); } - public async subscribe(id: string): Promise>> { - return await Api.post>(`games/${id}/readers`); + public async updateComment(commentId: string, comment: Comment): Promise>> { + return await Api.patch>(`games/comments/${commentId}`, comment); } - public async unsubscribe(id: string): Promise> { - return await Api.delete(`games/${id}/readers`); + public async deleteComment(commentId: string): Promise> { + return await Api.delete(`games/comments/${commentId}`); } }(); diff --git a/DM/Web/DM.Web.Modern/src/router.ts b/DM/Web/DM.Web.Modern/src/router.ts index 7bb32c73..a0506ed6 100644 --- a/DM/Web/DM.Web.Modern/src/router.ts +++ b/DM/Web/DM.Web.Modern/src/router.ts @@ -2,7 +2,7 @@ import Vue from 'vue'; import Router from 'vue-router'; import GeneralMenu from './views/layout/GeneralMenu.vue'; -import GameMenu from './views/pages/game/GameMenu.vue'; +import GameMenu from './views/pages/game/menu/GameMenu.vue'; import GeneralSidebar from './views/layout/GeneralSidebar.vue'; Vue.use(Router); @@ -173,10 +173,15 @@ export default new Router({ children: [{ name: 'game', path: '', - component: () => import('./views/pages/game/Information.vue'), + component: () => import('./views/pages/game/information/Information.vue'), }, { - name: 'game-comments', path: 'out-of-session', + component: () => import('./views/pages/game/comments/GameComments.vue'), + children: [{ + name: 'game-comments', + path: ':n?', + component: () => import('./views/pages/game/comments/GameCommentsList.vue'), + }], }, { name: 'create-character', path: 'create-character', diff --git a/DM/Web/DM.Web.Modern/src/store/forum/actions.ts b/DM/Web/DM.Web.Modern/src/store/forum/actions.ts index 675baada..2d628556 100644 --- a/DM/Web/DM.Web.Modern/src/store/forum/actions.ts +++ b/DM/Web/DM.Web.Modern/src/store/forum/actions.ts @@ -83,7 +83,7 @@ const actions: ActionTree = { commit('updateComment', data?.resource); } }, - async deleteComment(_0, { id }): Promise { + async deleteComment(_, { id }): Promise { await forumApi.deleteComment(id); }, async markAllTopicsAsRead({ commit }, { id }): Promise { diff --git a/DM/Web/DM.Web.Modern/src/store/gaming/actions.ts b/DM/Web/DM.Web.Modern/src/store/gaming/actions.ts index 6d198204..cfd10600 100644 --- a/DM/Web/DM.Web.Modern/src/store/gaming/actions.ts +++ b/DM/Web/DM.Web.Modern/src/store/gaming/actions.ts @@ -2,7 +2,8 @@ import { ActionTree } from 'vuex'; import gamingApi from '@/api/requests/gamingApi'; import GamingState from './gamingState'; import RootState from './../rootState'; -import { AttributeSchema } from '@/api/models/gaming'; +import {AttributeSchema, Comment} from '@/api/models/gaming'; +import { PagingQuery } from '@/api/models/common'; const actions: ActionTree = { async fetchOwnGames({ commit }): Promise { @@ -10,7 +11,7 @@ const actions: ActionTree = { commit('updateOwnGames', games); }, - async fetchPopularGames({commit}): Promise { + async fetchPopularGames({ commit }): Promise { const { resources: games } = await gamingApi.getPopularGames(); commit('updatePopularGames', games); }, @@ -44,15 +45,6 @@ const actions: ActionTree = { $router.push({ name: 'game', params: { id: game.id } }); } }, - async createCharacter({ state }, { character, $router }): Promise { - const { data, error } = await gamingApi.createCharacter(state.selectedGame!.id, character); - if (error) { - $router.push({ name: 'error', params: { code: error.code } }); - } else { - const { resource: character } = data!; - $router.push({ name: 'game-characters', params: { id: state.selectedGame!.id, characterId: character.id } }) - } - }, async selectGame({ commit }, { id, router }): Promise { commit('updateSelectedGame', null); @@ -65,26 +57,16 @@ const actions: ActionTree = { commit('updateSelectedGame', game); } }, - async fetchSelectedGameCharacters({ commit }, { id }): Promise { - commit('updateSelectedGameCharacters', null); - const { resources: characters } = await gamingApi.getCharacters(id); - commit('updateSelectedGameCharacters', characters); - }, - async fetchSelectedGameRooms({ commit }, { id }): Promise { - commit('updateSelectedGameRooms', null); - const { resources: rooms } = await gamingApi.getRooms(id); - commit('updateSelectedGameRooms', rooms); - }, + async fetchSelectedGameReaders({ commit }, { id }): Promise { commit('updateSelectedGameReaders', null); const { resources: readers } = await gamingApi.getReaders(id); commit('updateSelectedGameReaders', readers); }, - async subscribe({ commit, state }, { router }): Promise { const { data, error } = await gamingApi.subscribe(state.selectedGame!.id); if (error) { - router.push({ name: 'error', params: { code: error.code }}); + router.push({ name: 'error', params: { code: error.code } }); } else { const { resource: reader } = data!; commit('addReader', reader); @@ -93,11 +75,55 @@ const actions: ActionTree = { async unsubscribe({ commit, state, rootState }, { router }): Promise { const { error } = await gamingApi.unsubscribe(state.selectedGame!.id); if (error) { - router.push({ name: 'error', params: { code: error.code }}); + router.push({ name: 'error', params: { code: error.code } }); } else { commit('removeReader', rootState.user); } }, + + async fetchSelectedGameCharacters({ commit }, { id }): Promise { + commit('updateSelectedGameCharacters', null); + const { resources: characters } = await gamingApi.getCharacters(id); + commit('updateSelectedGameCharacters', characters); + }, + async createCharacter({ state }, { character, $router }): Promise { + const { data, error } = await gamingApi.createCharacter(state.selectedGame!.id, character); + if (error) { + $router.push({ name: 'error', params: { code: error.code } }); + } else { + const { resource: character } = data!; + $router.push({ name: 'game-characters', params: { id: state.selectedGame!.id, characterId: character.id } }) + } + }, + + async fetchSelectedGameComments({ commit }, { id, n }): Promise { + commit('updateSelectedGameComments', null); + const data = await gamingApi.getComments(id, { number: n } as PagingQuery); + commit('updateSelectedGameComments', data!); + }, + async createComment({ state }, { comment, $router }): Promise { + const { id } = state.selectedGame!; + const { error: postCommentError } = await gamingApi.createComment(id, comment); + if (postCommentError) return Promise.reject(); + + const data = await gamingApi.getComments(id, { size: 0, number: 0, skip: 0 }); + $router.push({ name: 'game-comments', params: { id, n: data.paging!.total } }); + }, + async updateComment({ commit }, { id, comment }): Promise { + const { data, error } = await gamingApi.updateComment(id, comment as Comment); + if (!error) { + commit('updateComment', data!.resource); + } + }, + async deleteComment(_, { id }) { + await gamingApi.deleteComment(id); + }, + + async fetchSelectedGameRooms({ commit }, { id }): Promise { + commit('updateSelectedGameRooms', null); + const { resources: rooms } = await gamingApi.getRooms(id); + commit('updateSelectedGameRooms', rooms); + }, }; export default actions; diff --git a/DM/Web/DM.Web.Modern/src/store/gaming/gamingState.ts b/DM/Web/DM.Web.Modern/src/store/gaming/gamingState.ts index 21e0cddb..a6760731 100644 --- a/DM/Web/DM.Web.Modern/src/store/gaming/gamingState.ts +++ b/DM/Web/DM.Web.Modern/src/store/gaming/gamingState.ts @@ -1,5 +1,6 @@ -import {Game, AttributeSchema, Tag, Character, Room} from '@/api/models/gaming'; -import {User} from '@/api/models/community'; +import { Game, AttributeSchema, Tag, Character, Room, Comment } from '@/api/models/gaming'; +import { User } from '@/api/models/community'; +import { ListEnvelope } from '@/api/models/common'; export default interface GamingState { ownGames: Game[] | null; @@ -12,4 +13,5 @@ export default interface GamingState { selectedGameCharacters: Character[] | null; selectedGameRooms: Room[] | null; selectedGameReaders: User[] | null; + selectedGameComments: ListEnvelope | null; } diff --git a/DM/Web/DM.Web.Modern/src/store/gaming/getters.ts b/DM/Web/DM.Web.Modern/src/store/gaming/getters.ts index a8f8a0e3..5311cac9 100644 --- a/DM/Web/DM.Web.Modern/src/store/gaming/getters.ts +++ b/DM/Web/DM.Web.Modern/src/store/gaming/getters.ts @@ -13,6 +13,7 @@ const getters: GetterTree = { selectedGameCharacters: (state) => state.selectedGameCharacters, selectedGameRooms: (state) => state.selectedGameRooms, selectedGameReaders: (state) => state.selectedGameReaders, + selectedGameComments: (state) => state.selectedGameComments, }; export default getters; diff --git a/DM/Web/DM.Web.Modern/src/store/gaming/index.ts b/DM/Web/DM.Web.Modern/src/store/gaming/index.ts index 60ef122d..1f5fbc40 100644 --- a/DM/Web/DM.Web.Modern/src/store/gaming/index.ts +++ b/DM/Web/DM.Web.Modern/src/store/gaming/index.ts @@ -15,6 +15,7 @@ const state: GamingState = { selectedGameCharacters: null, selectedGameRooms: null, selectedGameReaders: null, + selectedGameComments: null, }; const gaming: Module = { diff --git a/DM/Web/DM.Web.Modern/src/store/gaming/mutations.ts b/DM/Web/DM.Web.Modern/src/store/gaming/mutations.ts index 8ce129a0..3a037f63 100644 --- a/DM/Web/DM.Web.Modern/src/store/gaming/mutations.ts +++ b/DM/Web/DM.Web.Modern/src/store/gaming/mutations.ts @@ -1,7 +1,8 @@ import { MutationTree } from 'vuex'; import GamingState from './gamingState'; -import {Game, AttributeSchema, Tag, Character, Room, GameParticipation} from '@/api/models/gaming'; -import {User} from '@/api/models/community'; +import { Game, AttributeSchema, Tag, Character, Room, GameParticipation, Comment } from '@/api/models/gaming'; +import { User } from '@/api/models/community'; +import { ListEnvelope } from '@/api/models/common'; const mutations: MutationTree = { updateOwnGames(state, payload: Game[]) { @@ -34,6 +35,18 @@ const mutations: MutationTree = { updateSelectedGameReaders(state, payload: User[]) { state.selectedGameReaders = payload; }, + updateSelectedGameComments(state, payload: ListEnvelope) { + state.selectedGameComments = payload; + }, + updateComment(state, payload: Comment) { + state.selectedGameComments!.resources = state.selectedGameComments!.resources.map(comment => { + if (comment.id === payload.id) { + return payload; + } + + return comment; + }); + }, addReader(state, payload: User) { if (state.selectedGame === null || state.selectedGameReaders === null) return; diff --git a/DM/Web/DM.Web.Modern/src/views/layout/menu/MenuLink.vue b/DM/Web/DM.Web.Modern/src/views/layout/menu/MenuLink.vue index 2d3555b3..059e4586 100644 --- a/DM/Web/DM.Web.Modern/src/views/layout/menu/MenuLink.vue +++ b/DM/Web/DM.Web.Modern/src/views/layout/menu/MenuLink.vue @@ -3,7 +3,7 @@ {{ game.title }} - + @@ -49,6 +49,10 @@ export default class MenuLink extends Vue { & a.router-link-active theme(color, $text) + font-weight bold + +.menu-game-item__counters + white-space nowrap .menu-game-item-separator display inline-block @@ -56,4 +60,4 @@ export default class MenuLink extends Vue { vertical-align top theme(color, $secondaryText) cursor default - \ No newline at end of file + diff --git a/DM/Web/DM.Web.Modern/src/views/pages/game/comments/CreateGameCommentForm.vue b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/CreateGameCommentForm.vue new file mode 100644 index 00000000..fe902651 --- /dev/null +++ b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/CreateGameCommentForm.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameComment.vue b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameComment.vue new file mode 100644 index 00000000..8044cd83 --- /dev/null +++ b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameComment.vue @@ -0,0 +1,120 @@ + + + + + \ No newline at end of file diff --git a/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameComments.vue b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameComments.vue new file mode 100644 index 00000000..0044ea1d --- /dev/null +++ b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameComments.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameCommentsList.vue b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameCommentsList.vue new file mode 100644 index 00000000..b1f209f7 --- /dev/null +++ b/DM/Web/DM.Web.Modern/src/views/pages/game/comments/GameCommentsList.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/DM/Web/DM.Web.Modern/src/views/pages/game/CharactersList.vue b/DM/Web/DM.Web.Modern/src/views/pages/game/information/CharactersList.vue similarity index 96% rename from DM/Web/DM.Web.Modern/src/views/pages/game/CharactersList.vue rename to DM/Web/DM.Web.Modern/src/views/pages/game/information/CharactersList.vue index ab95be91..abadc3dc 100644 --- a/DM/Web/DM.Web.Modern/src/views/pages/game/CharactersList.vue +++ b/DM/Web/DM.Web.Modern/src/views/pages/game/information/CharactersList.vue @@ -47,7 +47,6 @@