Skip to content

Commit

Permalink
Merge pull request #62 from kohei-s/sample-games
Browse files Browse the repository at this point in the history
Feature sample games and edit component
  • Loading branch information
kohei-s authored Sep 7, 2023
2 parents e8a64a3 + 8f0f0bb commit 9c36036
Show file tree
Hide file tree
Showing 16 changed files with 112 additions and 180 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@

# KoToKo: Japanese concentration game app
This mobile application is developed 🛠️ as my personal capstone project for [Java Development Bootcamp at neue fische](https://www.neuefische.de/en/bootcamp/java-development) from May to August 2023 (try the latest version 👉 [Kotoko](https://kotoko.de))
This mobile application is developed 🛠️ as my personal capstone project for [Java Development Bootcamp at neue fische](https://www.neuefische.de/en/bootcamp/java-development) from May to August 2023 (try the latest version 👉 [Kotoko](https://kotoko.de))

[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=kohei-s_kotoko-concentration-app-frontend&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=kohei-s_kotoko-concentration-app-frontend) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=kohei-s_kotoko-concentration-app-frontend&metric=coverage)](https://sonarcloud.io/summary/new_code?id=kohei-s_kotoko-concentration-app-frontend) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=kohei-s_kotoko-concentration-app-frontend&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=kohei-s_kotoko-concentration-app-frontend) \
\
![KoToKo_ver1](https://github.com/kohei-s/kotoko-concentration-app/assets/82062401/b4132c77-a729-490f-ac6c-150da692cf4d)
(device pixel ratio: 390x844)
## 1. Concept 📱
New concentration game app aims to support learning Japanese for so called [third culture kids](https://en.wikipedia.org/wiki/Third_culture_kid) growing up speaking a different language in kindergarten or school. The name of the app *KoToKO (言と言)* is a neologism that could mean *Word & Word*.
## 2. Problem 😦
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ public class GameCardsController {
this.gameCardsService = gameCardsService;
}

@GetMapping("/all")
List<GameCard> getAllGameCards() {

return gameCardsService.getAllGameCards();
}

@GetMapping("/myAll")
List<GameCard> getAllMyGameCards() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ public class GameCardsService {
this.mongoUserService = mongoUserService;
}

public List<GameCard> getAllGameCards(){

return gameCardsRepository.findAll();
}

public List<GameCard> getAllMyGameCards() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
String userId = mongoUserService.findUserIdByUsername(username);
Expand All @@ -42,7 +37,7 @@ public GameCard getGameCardById(String id){
}

public List<GameCard> findByCardSetName(String cardSetName){
List<GameCard> gameCardList = gameCardsRepository.findAll();
List<GameCard> gameCardList = getAllMyGameCards();

return gameCardList.stream().filter(card -> cardSetName.equals(card.getCardSetName())).toList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,6 @@ void setUpUsers() {
mongoUserRepository.save(user);
}

@DirtiesContext
@Test
@WithMockUser
void whenListEmpty_thenReturnEmptyList() throws Exception {
//WHEN
mockMvc.perform(
MockMvcRequestBuilders.get("/api/game_cards/all")
.with(csrf())
)

//THEN
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().json("""
[]
"""));
}

@DirtiesContext
@Test
@WithMockUser (username = "testUsername", password = "testPassword")
Expand Down Expand Up @@ -155,18 +138,18 @@ void whenGetNotExistingGameCard_thenReturnNotFoundErrorMessage() throws Exceptio
void expectTwoDimensionalArrayOfTwentyEightPlayingCards() throws Exception {
//GIVEN
String size = "small";
gameCardsRepository.save(new GameCard("01", "♥1", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("012", "♥2", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("03", "♥3", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("04", "♥4", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("05", "♥5", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("06", "♥6", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("07", "♥7", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("08", "♥8", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("09", "♥9", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("10", "♥10", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("11", "♥11", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("12", "♥12", "playing-cards", "testId"));
gameCardsRepository.save(new GameCard("01", "♥1", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("02", "♥2", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("03", "♥3", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("04", "♥4", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("05", "♥5", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("06", "♥6", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("07", "♥7", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("08", "♥8", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("09", "♥9", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("10", "♥10", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("11", "♥11", "playing-cards", "1"));
gameCardsRepository.save(new GameCard("12", "♥12", "playing-cards", "1"));

//WHEN
String result = mockMvc.perform(
Expand Down Expand Up @@ -209,7 +192,7 @@ void whenAddedGameCard_thenReturnGameCard() throws Exception {
.andExpect(jsonPath("id").isNotEmpty())
.andExpect(jsonPath("title").value("testTitle"))
.andExpect(jsonPath("cardSetName").value("testSet3"))
.andExpect(jsonPath("authorId").isNotEmpty());
.andExpect(jsonPath("authorId").value("1"));
}

@DirtiesContext
Expand Down Expand Up @@ -250,7 +233,7 @@ void whenUpdateExistingGameCard_thenReturnUpdatedGameCard() throws Exception {
{
"title": "test_updated",
"cardSetName": "testSet",
"authorId": "testId"
"authorId": "1"
}
""")
.with(csrf())
Expand All @@ -262,7 +245,7 @@ void whenUpdateExistingGameCard_thenReturnUpdatedGameCard() throws Exception {
.andExpect(jsonPath("id").value(id))
.andExpect(jsonPath("title").value("test_updated"))
.andExpect(jsonPath("cardSetName").value("testSet"))
.andExpect(jsonPath("authorId").value("testId"));
.andExpect(jsonPath("authorId").value("1"));
}

@DirtiesContext
Expand All @@ -271,7 +254,7 @@ void whenUpdateExistingGameCard_thenReturnUpdatedGameCard() throws Exception {
void whenUpdateNotExistingGameCard_thenReturnNotFoundErrorMessage() throws Exception {
//GIVEN
String idOfNotExistingGameCard = "012";
GameCardWithoutId gameCardWithoutId = new GameCardWithoutId("test", "testSet", "testId");
GameCardWithoutId gameCardWithoutId = new GameCardWithoutId("test", "testSet", "1");
String gameCardJson = objectMapper.writeValueAsString(gameCardWithoutId);

//WHEN
Expand Down Expand Up @@ -324,7 +307,7 @@ void whenDeleteExistingGameCard_thenReturnEmptyList() throws Exception {

//THEN
mockMvc.perform(
MockMvcRequestBuilders.get("/api/game_cards/all")
MockMvcRequestBuilders.get("/api/game_cards/myAll")
.with(csrf())
).andExpect(status().isOk())
.andExpect(content().json("[]"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,6 @@ class GameCardsGridServiceTest {
GameCard gameCard11 = new GameCard("11", "♥11", "1playing-cards", "testId");
GameCard gameCard12 = new GameCard("12", "♥12", "playing-cards", "testId");

@Test
void getAllGameCards_thenReturnListOfAllGameCards() {
//Given
List<GameCard> allGameCards = List.of(
gameCard1, gameCard2, gameCard3,
gameCard4, gameCard5, gameCard6,
gameCard7, gameCard8, gameCard9,
gameCard10, gameCard11, gameCard12);

//WHEN
Mockito.when(gameCardsRepository.findAll())
.thenReturn(allGameCards);
List<GameCard> expected = List.of(
gameCard1, gameCard2, gameCard3,
gameCard4, gameCard5, gameCard6,
gameCard7, gameCard8, gameCard9,
gameCard10, gameCard11, gameCard12);
List<GameCard> actual = gameCardsService.getAllGameCards();

//THEN
assertEquals(expected, actual);
}

@Test
void getAllMyGameCards_thenReturnListOfAllGameCards() {
//Given
Expand Down Expand Up @@ -126,20 +103,27 @@ void getNotExistingGameCardWithId_thenThrowException() {
void generateGameBoardSmallSize_thenReturnTwoDimensionalArrayOfTwelveGameCards() {
//Given
String id = "012";
List<GameCard> allGameCards = List.of(
List<GameCard> allMyGameCards = List.of(
gameCard1, gameCard2, gameCard3,
gameCard4, gameCard5, gameCard6,
gameCard7, gameCard8, gameCard9,
gameCard10, gameCard11, gameCard12);

//WHEN
Mockito.when(gameCardsRepository.findAll())
.thenReturn(allGameCards);
Mockito.when(idService.createRandomId())
.thenReturn(id);
Mockito.when(authentication.getName())
.thenReturn("testName");
Mockito.when(securityContext.getAuthentication())
.thenReturn(authentication);
Mockito.when(mongoUserService.findUserIdByUsername("testName"))
.thenReturn("testId");
SecurityContextHolder.setContext(securityContext);
Mockito.when(gameCardsRepository.findAllByAuthorId("testId"))
.thenReturn(allMyGameCards);
int expectedRow = 3;
int expectedColumn = 3;
GameCard[][] actual = gameCardsService.generateGameBoard("small", allGameCards);
GameCard[][] actual = gameCardsService.generateGameBoard("small", allMyGameCards);

//THEN
assertEquals(expectedRow, actual.length);
Expand Down
50 changes: 8 additions & 42 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,16 @@ export default function App() {

const [userName, setUserName] = useState<string>("")
const [userInfo, setUserInfo] = useState<UserInfo>()
const [allGameCards, setAllGameCards] = useState<GameCard[]>([]);
const [allCardSets, setAllCardSets] = useState<GameCardSet[]>([]);
const [allMyGameCards, setAllMyGameCards] = useState<GameCard[]>([]);
const [allMyCardSets, setMyAllCardSets] = useState<GameCardSet[]>([]);
const navigate = useNavigate()

useEffect(() => {
getAllSetNamesAndItsCount()
getAllMySetNamesAndItsCount()
}, [allGameCards, allMyGameCards]);
}, [allMyGameCards]);

useEffect(() => {
me()
loadAllGameCards()
getAllSetNamesAndItsCount()
loadAllMyGameCards()
getAllMySetNamesAndItsCount()
}, [userName]);
Expand Down Expand Up @@ -108,37 +103,6 @@ export default function App() {
.catch(console.error)
}

function loadAllGameCards() {
axios.get<GameCard[]>(
"/api/game_cards/all"
)
.then(response => response.data)
.then(data => {
const responseDataCardList = data
responseDataCardList.reverse()
setAllGameCards(responseDataCardList)
})
.catch(console.error)
}

function getAllSetNamesAndItsCount() {
const listAllSetNames = allGameCards.map((card) => card.cardSetName)
const countList: { [key: string]: number } = {};
for (const item of listAllSetNames) {
if (countList[item]) {
countList[item] += 1;
} else {
countList[item] = 1;
}
}
const filteredList: GameCardSet[] = [];
for (const [key, value] of Object.entries(countList)) {
const newSet: GameCardSet = {name: key, count: value};
filteredList.push(newSet)
}
setAllCardSets(filteredList)
}

function loadAllMyGameCards() {
axios.get<GameCard[]>(
"/api/game_cards/myAll"
Expand Down Expand Up @@ -167,6 +131,10 @@ export default function App() {
const newMySet: GameCardSet = {name: key, count: value, author: userName};
filteredMyList.push(newMySet)
}
const kanjiSet: GameCardSet = {name: "kanji", count: 11, author: "sample"};
const animalSet: GameCardSet = {name: "animal", count: 8, author: "sample"}
filteredMyList.push(kanjiSet)
filteredMyList.push(animalSet)
setMyAllCardSets(filteredMyList)
}

Expand All @@ -183,18 +151,16 @@ export default function App() {
<Route path={"/game/:gameSize/:gameName"}
element={<GameBoard userInfo={userInfo} update={update}/>}></Route>
<Route path={"/collection"}
element={<GameCardCollection allGameCards={allGameCards}
loadAllGameCards={loadAllGameCards}
allCardSets={allCardSets}
allMyGameCards={allMyGameCards}
element={<GameCardCollection allMyGameCards={allMyGameCards}
loadAllMyGameCards={loadAllMyGameCards}
allMyCardSets={allMyCardSets}/>}></Route>
<Route path={"/edit/:setName/:number"}
element={<EditGameCard allMyGameCards={allMyGameCards}
allMyCardSets={allMyCardSets}
loadAllMyGameCards={loadAllMyGameCards}/>}></Route>
<Route path={"/record"} element={<GameRecord userInfo={userInfo}/>}></Route>
<Route path={"/setting"} element={<Setting userInfo={userInfo} update={update}
countCardSets={allCardSets}/>}></Route>
countCardSets={allMyCardSets}/>}></Route>
<Route path={"/*"} element={<Navigate to={"/"}/>}/>
</Route>
</Routes>
Expand Down
9 changes: 2 additions & 7 deletions frontend/src/Collection/GameCardCollection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,16 @@ import GameCardSetTable from "./GameCardSetTable.tsx";
import {GameCardSet} from "./GameCardSet.ts";

type Props = {
allGameCards: GameCard[]
loadAllGameCards: () => void
allCardSets: GameCardSet[]
allMyGameCards: GameCard[]
loadAllMyGameCards: () => void
allMyCardSets: GameCardSet[]
}

export default function GameCardCollection(props: Props) {

/*
const [, setAllCardSetNames] = useState<string[]>([]);*/
const [isModalOpen, setIsModalOpen] = useState(false);

if (!props.allGameCards) {
if (!props.allMyGameCards) {
return "Loading cards..."
}

Expand Down Expand Up @@ -53,7 +48,7 @@ export default function GameCardCollection(props: Props) {
<NewGameCard onClose={closeModal} onAddNewCard={props.loadAllMyGameCards}/>
</div>
</Modal>
<GameCardSetTable allCardSets={props.allCardSets} allMyCardSets={props.allMyCardSets}></GameCardSetTable>
<GameCardSetTable allMyCardSets={props.allMyCardSets}></GameCardSetTable>
</>
)

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/Collection/GameCardSet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type GameCardSet = {
name: string,
count: number,
author?: string
}
author: string
}
19 changes: 10 additions & 9 deletions frontend/src/Collection/GameCardSetTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {GameCardSet} from "./GameCardSet.ts";
import {Link} from "react-router-dom";

type Props = {
allCardSets: GameCardSet[],
allMyCardSets: GameCardSet[]
}
export default function GameCardSetTable(props: Props) {
Expand Down Expand Up @@ -52,16 +51,18 @@ export default function GameCardSetTable(props: Props) {
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<StyledTableCell component="th" scope="row">
{row.name}
{row.name} {((row.name === "kanji")||(row.name === "animal"))? "(sample)": ""}
</StyledTableCell>
<StyledTableCell align="right">{row.count}</StyledTableCell>
<StyledTableCell align="right">
<Link to={"/edit/" + row.name + "/" + row.count.toString()}>
<IconButton size="small" disableRipple={true} className={"buttonAdd"}
sx={{color: "#0c6b18", boxShadow: 0}}>
<EditIcon fontSize={"small"}/>
</IconButton>
</Link>
{(row.author === "sample") ? "" :
<Link to={"/edit/" + row.name + "/" + row.count.toString()}>
<IconButton size="small" disableRipple={true} className={"buttonAdd"}
sx={{color: "#0c6b18", boxShadow: 0}}>
<EditIcon fontSize={"small"}/>
</IconButton>
</Link>
}
</StyledTableCell>
</StyledTableRow>
))}
Expand All @@ -71,4 +72,4 @@ export default function GameCardSetTable(props: Props) {
</>
)

}
}
Loading

0 comments on commit 9c36036

Please sign in to comment.