Skip to content

Commit

Permalink
Username change (#64)
Browse files Browse the repository at this point in the history
* make user able to update their username

* update to lodestone scraper to let it run on 3.8

* added user details form to edit the username

* version bump
  • Loading branch information
freyamade authored Dec 7, 2023
1 parent 6fce450 commit f84e2e9
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 13 deletions.
3 changes: 2 additions & 1 deletion backend/api/lodestone_scraper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# stdlib
import logging
import re
from typing import Optional
# lib
import requests
from bs4 import BeautifulSoup
Expand Down Expand Up @@ -46,7 +47,7 @@ def get_instance(cls) -> 'LodestoneScraper':
cls._instance = instance
return instance

def check_token(self, character_id: str, token: str) -> str | None:
def check_token(self, character_id: str, token: str) -> Optional[str]:
"""
Check the given character for the specified token being present in their bio.
Return an error string to pass back to the FE if not found, or None if it was.
Expand Down
3 changes: 2 additions & 1 deletion backend/api/serializers/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@


class SettingsSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=150)

class Meta:
model = Settings
fields = ['loot_manager_version', 'notifications', 'theme']
fields = ['loot_manager_version', 'notifications', 'theme', 'username']

def validate_notifications(self, notifications: Dict[str, bool]) -> Dict[str, bool]:
"""
Expand Down
6 changes: 4 additions & 2 deletions backend/api/tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ def test_update(self):
user = self._get_user()
self.client.force_authenticate(user)

data = {'theme': 'blue', 'notifications': {'verify_fail': False}, 'loot_manager_version': 'fight'}
data = {'theme': 'blue', 'notifications': {'verify_fail': False}, 'loot_manager_version': 'fight', 'username': 'abcde'}
response = self.client.put(url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response)
user.refresh_from_db()
self.assertEqual(user.settings.loot_manager_version, 'fight')
self.assertEqual(user.settings.theme, 'blue')
self.assertFalse(user.settings.notifications['verify_fail'])
self.assertEqual(user.get_full_name(), data['username'])

# Run it again to hit the other block
data = {'theme': 'purple', 'notifications': {'verify_success': True}}
data = {'theme': 'purple', 'notifications': {'verify_success': True}, 'username': 'abcde'}
response = self.client.put(url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
user.refresh_from_db()
Expand All @@ -78,6 +79,7 @@ def test_update_400(self):
data = {'theme': 'abcde', 'notifications': {'abcde': 'abcde'}, 'loot_manager_version': 'abcde'}
response = self.client.put(url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json()['username'], ['This field is required.'])
self.assertEqual(response.json()['notifications'], ['"abcde" is not a valid choice.'])
self.assertEqual(response.json()['loot_manager_version'], ['"abcde" is not a valid choice.'])

Expand Down
4 changes: 4 additions & 0 deletions backend/api/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def put(self, request) -> Response:
obj.loot_manager_version = serializer.validated_data.get('loot_manager_version', obj.loot_manager_version)
obj.save()

# Update the username
request.user.first_name = serializer.validated_data.get('username', request.user.first_name)
request.user.save()

# Send websocket packet for updates
self._send_to_user(request.user, {'type': 'settings'})

Expand Down
2 changes: 1 addition & 1 deletion backend/backend/settings_live.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def sampler(context):
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True,
release='savageaim@20231205',
release='savageaim@20231207',
)

# Channels
Expand Down
2 changes: 1 addition & 1 deletion frontend/.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VUE_APP_VERSION="20231205"
VUE_APP_VERSION="20231207"
5 changes: 5 additions & 0 deletions frontend/src/components/modals/changelog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
</div>
<div class="card-content content">
<h2 class="has-text-primary subtitle">{{ version }}</h2>
<div class="divider"><i class="material-icons icon">expand_more</i> Settings Updates <i class="material-icons icon">expand_more</i></div>
<p>
Usernames are now editable on the User Settings page!
<ul><li>For anyone who's usernames are different since the Discord Username update, this will probably help!</li></ul>
</p>

<div class="divider"><i class="material-icons icon">expand_more</i> Major Fixes <i class="material-icons icon">expand_more</i></div>
<p>
Expand Down
44 changes: 44 additions & 0 deletions frontend/src/components/settings/user_details.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<div class="card">
<div class="card-header">
<div class="card-header-title">User Details</div>
</div>
<div class="card-content">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Username</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" :class="{'is-danger': errors.username !== undefined}" :value="username" ref="input" @input="changeUsername" />
</div>
<p v-if="errors.username !== undefined" class="help is-danger">{{ errors.username[0] }}</p>
</div>
</div>
</div>
</div>
</div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { SettingsErrors } from '@/interfaces/responses'
@Component
export default class UserDetailsSettings extends Vue {
@Prop()
errors!: SettingsErrors
@Prop()
username!: string
get input(): HTMLInputElement {
return this.$refs.input as HTMLInputElement
}
changeUsername(): void {
this.$emit('changeUsername', this.input.value)
}
}
</script>
2 changes: 1 addition & 1 deletion frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Sentry.init({
Vue,
dsn: 'https://[email protected]/6180221',
logErrors: true,
release: 'savageaim@20231205',
release: 'savageaim@20231207',
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
Expand Down
48 changes: 42 additions & 6 deletions frontend/src/views/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
<div class="column is-one-quarter-desktop">
<div class="card">
<div class="card-header">
<div class="card-header-title">Settings for {{ user.username }}</div>
<div class="card-header-title">User Settings</div>
</div>
<div class="card-content">
<aside class="menu">
<ul class="menu-list">
<li>
<a :class="{ 'is-active': activeTab.details }" @click="showDetails">
User Details <span v-if="unsavedDetails()">*</span>
</a>
</li>
<li>
<a :class="{ 'is-active': activeTab.theme }" @click="showTheme">
Colour Scheme <span v-if="unsavedTheme()">*</span>
Expand All @@ -34,14 +39,20 @@

<!-- Notifications -->
<div class="column">
<UserDetailsSettings
:errors="errors"
:username="username"
v-on:changeUsername="changeUsername"
v-if="activeTab.details"
/>
<!-- Colour Scheme -->
<ThemeSettings
:errors="errors"
:theme="theme"
v-on:changeTheme="changeTheme"
v-if="activeTab.theme"
/>
<NotificationsSettings
<NotificationsSettingsComponent
:notifications="notifications"
v-on:changeNotification="changeNotification"
v-if="activeTab.notifications"
Expand All @@ -62,8 +73,9 @@ import isEqual from 'lodash.isequal'
import * as Sentry from '@sentry/vue'
import { Component, Watch } from 'vue-property-decorator'
import LootManagerSettings from '@/components/settings/loot_manager.vue'
import NotificationsSettings from '@/components/settings/notifications.vue'
import NotificationsSettingsComponent from '@/components/settings/notifications.vue'
import ThemeSettings from '@/components/settings/theme.vue'
import UserDetailsSettings from '@/components/settings/user_details.vue'
import NotificationSettings from '@/interfaces/notification_settings'
import { SettingsErrors } from '@/interfaces/responses'
import User from '@/interfaces/user'
Expand All @@ -72,13 +84,15 @@ import SavageAimMixin from '@/mixins/savage_aim_mixin'
@Component({
components: {
LootManagerSettings,
NotificationsSettings,
NotificationsSettingsComponent,
ThemeSettings,
UserDetailsSettings,
},
})
export default class Settings extends SavageAimMixin {
activeTab = {
theme: true,
details: true,
theme: false,
notifications: false,
lootManager: false,
}
Expand All @@ -93,6 +107,8 @@ export default class Settings extends SavageAimMixin {
theme = this.user.theme
username = this.user.username
mounted(): void {
document.title = 'User Settings - Savage Aim'
}
Expand All @@ -119,6 +135,10 @@ export default class Settings extends SavageAimMixin {
this.theme = theme
}
changeUsername(username: string): void {
this.username = username
}
// Function called on page reload via websockets
async load(): Promise<void> {
// This function does nothing on purpose
Expand All @@ -132,14 +152,21 @@ export default class Settings extends SavageAimMixin {
}
resetActiveTab(): void {
this.activeTab.details = false
this.activeTab.theme = false
this.activeTab.notifications = false
this.activeTab.lootManager = false
}
// Save the data into a new bis list
async save(): Promise<void> {
const body = JSON.stringify({ theme: this.theme, notifications: this.notifications, loot_manager_version: this.lootManagerVersion })
const body = JSON.stringify({
theme: this.theme,
notifications: this.notifications,
loot_manager_version: this.lootManagerVersion,
username: this.username,
})
try {
const response = await fetch(this.url, {
method: 'PUT',
Expand Down Expand Up @@ -167,6 +194,11 @@ export default class Settings extends SavageAimMixin {
}
}
showDetails(): void {
this.resetActiveTab()
this.activeTab.details = true
}
showLootManager(): void {
this.resetActiveTab()
this.activeTab.lootManager = true
Expand All @@ -182,6 +214,10 @@ export default class Settings extends SavageAimMixin {
this.activeTab.theme = true
}
unsavedDetails(): boolean {
return this.username !== this.user.username
}
unsavedLootManager(): boolean {
return this.lootManagerVersion !== this.user.loot_manager_version
}
Expand Down

0 comments on commit f84e2e9

Please sign in to comment.