Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/store language in db #31

Merged
merged 7 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# OS
.DS_Store

# Nginx
default.conf

# vite
node_modules/

Expand Down
2 changes: 1 addition & 1 deletion backend/transcendence/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

setup_logging(level=logging.INFO)
setup_logging(level=logging.DEBUG)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/

Expand Down
10 changes: 9 additions & 1 deletion backend/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@


class User(models.Model):
LANGUAGE_CHOICES = [
(0, "EN"),
(1, "KR"),
(2, "JP"),
]

user_id = models.IntegerField(primary_key=True)
nickname = models.CharField(max_length=255)
email = models.EmailField(max_length=255)
Expand All @@ -10,9 +16,10 @@ class User(models.Model):
is_online = models.BooleanField(default=False)
win = models.IntegerField(default=0)
lose = models.IntegerField(default=0)
language = models.IntegerField(choices=LANGUAGE_CHOICES, default=0)

def __str__(self):
return self.nickname + '#' + str(self.user_id)
return self.nickname + "#" + str(self.user_id)


class Friend(models.Model):
Expand All @@ -39,6 +46,7 @@ def __str__(self):
user2_name = self.user2.nickname if self.user2 else "Unknown"
return f"Game {self.game_id}: {user1_name} vs {user2_name}"


class Tournament(models.Model):
tournament_id = models.AutoField(primary_key=True)
game_id1 = models.ForeignKey(Game, null=True, on_delete=models.SET_NULL, related_name="tournaments_as_game1")
Expand Down
16 changes: 4 additions & 12 deletions backend/users/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ class Meta:
model = User
fields = ["user_id", "nickname", "img_url", "is_2FA", "is_online", "win", "lose"]

class LanguageSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["language"]

class FriendSerializer(serializers.ModelSerializer):
class Meta:
Expand All @@ -16,15 +20,3 @@ class Meta:

class FriendRequestSerializer(serializers.Serializer):
user_id = serializers.IntegerField()


class GameSerializer(serializers.ModelSerializer):
class Meta:
model = Game
fields = ["game_id", "game_type", "user1", "user2", "score1", "score2", "start_timestamp", "end_timestamp"]


class TournamentSerializer(serializers.ModelSerializer):
class Meta:
model = Tournament
fields = ["name", "date", "winner"]
2 changes: 2 additions & 0 deletions backend/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from .views import (
UserDetailView,
FriendDetailView,
LanguageView,
get_user,
get_user_list,
)

urlpatterns = [
path('me/', UserDetailView.as_view()),
path('friends/', FriendDetailView.as_view()),
path('language/', LanguageView.as_view()),
path('user/', get_user_list),
path('user/<int:pk>/', get_user),
]
33 changes: 32 additions & 1 deletion backend/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import User, Friend, Game, Tournament
from .serializers import UserSerializer, FriendSerializer, FriendRequestSerializer, GameSerializer, TournamentSerializer
from .serializers import (
UserSerializer,
LanguageSerializer,
FriendSerializer,
FriendRequestSerializer,
)
from login.views import decode_jwt
from drf_yasg.utils import swagger_auto_schema

Expand Down Expand Up @@ -56,6 +61,32 @@ def delete(self, request):
return response


class LanguageView(APIView):
def get(self, request):
payload = decode_jwt(request)
if not payload:
return Response(status=status.HTTP_401_UNAUTHORIZED)

user = get_object_or_404(User, pk=payload.get("id"))
return Response({"language": user.language})

@swagger_auto_schema(request_body=LanguageSerializer, responses={200: LanguageSerializer()})
def put(self, request):
payload = decode_jwt(request)
if not payload:
return Response(status=status.HTTP_401_UNAUTHORIZED)

user = get_object_or_404(User, pk=payload.get("id"))
print(request.data.get("language"))
serializer = LanguageSerializer(user, data=request.data, partial=True)

if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class FriendDetailView(APIView):
def get(self, request):
payload = decode_jwt(request)
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ class App {
constructor() {
this.app = document.querySelector("#app");
this.lan = { value: 0 };
console.log("start!!");
console.log(this.lan);
}
}

Expand Down
20 changes: 18 additions & 2 deletions frontend/src/components/2FA.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,24 @@ export class TwoFA extends Component {
.then(data => {
if (data) {
if (data.success) {
console.log("code good!");
changeUrl('/main'); // 메인 페이지로 이동
// API!!! jwt가 있으면 해당 유저의 데이터베이스에서 언어 번호 (0 or 1 or 2) 얻어오기
fetch("https://localhost:443/api/language/", {
method: 'GET',
credentials: 'include', // 쿠키를 포함하여 요청 (사용자 인증 필요 시)
})
.then(response => {
if (!response.ok){
changeUrl("/");
return null;
}
return response.json();
})
.then(data => {
if (data){
this.props.lan.value = data.language;
changeUrl('/main'); // 메인 페이지로 이동
}
});
} else {
messageElement.textContent = "Invalid code. Please try again.";
messageElement.classList.remove('text-success');
Expand Down
51 changes: 39 additions & 12 deletions frontend/src/components/Main-Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@ export class Menu extends Component {
0: {
gameMenuTexts: ["Local Game", "Multi Game", "Tournament"],
userMenuTexts: ["Friends", "Profile", "Logout"],
lanText: "Change Language"
},
1: {
gameMenuTexts: ["로컬 게임", "멀티 게임", "토너먼트"],
userMenuTexts: ["친구", "프로필", "로그아웃"],
lanText: "언어 변경"
},
2: {
gameMenuTexts: ["ローカルゲーム", "マルチゲーム", "トーナメント"],
userMenuTexts: ["友達", "プロフィール", "ログアウト"],
lanText: "言語を変更"
}
};

Expand All @@ -36,7 +33,9 @@ export class Menu extends Component {

return `
<div id="menuBox">
<div id="lanButton">${translations.lanText}</div>
<div id="enButton">English</div>
<div id="koButton">한국어</div>
<div id="jpButton">日本語</div>
<ul id="gameMenu"></ul>
<ul id="userMenu"></ul>
</div>
Expand All @@ -47,27 +46,55 @@ export class Menu extends Component {
new List(document.querySelector("ul#gameMenu"), { className: "gameMode", ids: ["LocalGame", "MultiGame", "Tournament"], contents: this.translations.gameMenuTexts});
new List(document.querySelector("ul#userMenu"), { className: "showInfo", ids: ["Friends", "Profile", "Logout"], contents: this.translations.userMenuTexts});
}

setEvent () {

this.addEvent('click', '#Friends', () => {
changeUrl("/main/friends");
});

this.addEvent('click', '#LocalGame', () => {
changeUrl(`/game/local/${this.uid}`);
});

this.addEvent('click', "#MultiGame", () => {
changeUrl("/main/matching");
});

this.addEvent('click', "#Tournament", () => {
changeUrl("/main/tournament");
});

this.addEvent('click', '#lanButton', () => {
this.props.lan.value = (this.props.lan.value + 1) % 3;
changeUrl("/main")

function storeLang(value) {
fetch("https://localhost:443/api/language/", {
method: 'PUT',
credentials: 'include', // 쿠키를 포함하여 요청 (사용자 인증 필요 시)
headers: {
'Content-Type': 'application/json' // JSON 데이터임을 명시
},
body: JSON.stringify({
language: value
})
})
.then(response => {
if (!response.ok) changeUrl("/");
})
changeUrl("/main");
}

this.addEvent('click', '#enButton', () => {
this.props.lan.value = 0;
storeLang(this.props.lan.value);
});

this.addEvent('click', '#koButton', () => {
this.props.lan.value = 1;
storeLang(this.props.lan.value);
});

this.addEvent('click', '#jpButton', () => {
this.props.lan.value = 2;
storeLang(this.props.lan.value);
});

this.addEvent('click', '#Profile', () => {
Expand Down
38 changes: 36 additions & 2 deletions frontend/src/core/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,25 @@ export async function parsePath(path) {
return changeUrl("/", false);
});
} else {
return changeUrl("/main", false);
// API!!! jwt가 있으면 해당 유저의 데이터베이스에서 언어 번호 (0 or 1 or 2) 얻어오기
fetch("https://localhost:443/api/language/", {
method: 'GET',
credentials: 'include', // 쿠키를 포함하여 요청 (사용자 인증 필요 시)
})
.then(response => {
if (!response.ok){
changeUrl("/");
return null;
}
return response.json();
})
.then(data => {
if (data){
console.log(data.language);
root.lan.value = data.language;
changeUrl('/main'); // 메인 페이지로 이동
}
});
}
}
else return changeUrl("/", false);
Expand Down Expand Up @@ -136,7 +154,23 @@ export const initializeRouter = () => {
window.addEventListener("popstate", async () => {
await parsePath(window.location.pathname);
});
parsePath(window.location.pathname);
fetch("https://localhost:443/api/language/", {
method: 'GET',
credentials: 'include', // 쿠키를 포함하여 요청 (사용자 인증 필요 시)
})
.then(response => {
if (!response.ok){
console.log("so bad");
return null;
}
return response.json();
})
.then(data => {
if (data){
root.lan.value = data.language;
}
parsePath(window.location.pathname);
});
};

async function checkAuth() {
Expand Down
57 changes: 54 additions & 3 deletions frontend/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,65 @@ div#menuBox {
justify-content: center;
}

div#lanButton {

div#enButton {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-weight: 400;
border-radius: 10px;
width: 100px;
height: 50px;
bottom: 30px;
right: 270px;
font-size: 17px;
border-radius: 50px;
color: white;
cursor: pointer;
background: #357ABD;
box-shadow: 0 0 0 rgba(0, 0, 0, 0); /* 기본 상태에서 그림자 없음 */
transition: box-shadow 0.3s ease, transform 0.3s ease;
}

div#enButton:hover {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); /* 호버 시 더 깊은 그림자 */
transform: translateY(-2px); /* 살짝 위로 이동 */
}

div#koButton {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-weight: 400;
border-radius: 10px;
width: 160px;
width: 100px;
height: 50px;
bottom: 30px;
right: 150px;
font-size: 17px;
border-radius: 50px;
color: white;
cursor: pointer;
background: #357ABD;
box-shadow: 0 0 0 rgba(0, 0, 0, 0); /* 기본 상태에서 그림자 없음 */
transition: box-shadow 0.3s ease, transform 0.3s ease;
}

div#koButton:hover {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); /* 호버 시 더 깊은 그림자 */
transform: translateY(-2px); /* 살짝 위로 이동 */
}

div#jpButton {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-weight: 400;
border-radius: 10px;
width: 100px;
height: 50px;
bottom: 30px;
right: 30px;
Expand All @@ -194,7 +245,7 @@ div#lanButton {
transition: box-shadow 0.3s ease, transform 0.3s ease;
}

div#lanButton:hover {
div#enButton:hover {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); /* 호버 시 더 깊은 그림자 */
transform: translateY(-2px); /* 살짝 위로 이동 */
}
Expand Down
Loading
Loading