From 57663b802c8f50e368661716455fa56d89298229 Mon Sep 17 00:00:00 2001 From: Mitchell Gerber Date: Thu, 11 Jan 2018 23:59:56 -0600 Subject: [PATCH] client - wip user account page --- client/app/axios/axios.ts | 2 +- .../content-container/content-container.tsx | 3 +- .../components/login-button/login-button.tsx | 23 +++- client/app/components/portrait/portrait.tsx | 3 +- client/app/model/avatar.ts | 71 ++++++++++ client/app/model/character.ts | 13 ++ client/app/model/index.ts | 2 + client/app/model/user.ts | 5 + client/app/pages/forum/forum.tsx | 2 +- .../app/pages/user-account/user-account.scss | 20 +++ .../app/pages/user-account/user-account.tsx | 127 +++++++++++++++++- client/app/services/user.service.ts | 27 +++- client/app/stores/user-store.ts | 22 ++- lib/myapp/data/user.ex | 2 +- 14 files changed, 303 insertions(+), 19 deletions(-) create mode 100644 client/app/model/avatar.ts create mode 100644 client/app/model/character.ts create mode 100644 client/app/pages/user-account/user-account.scss diff --git a/client/app/axios/axios.ts b/client/app/axios/axios.ts index 000d0ad..3cf750b 100644 --- a/client/app/axios/axios.ts +++ b/client/app/axios/axios.ts @@ -40,7 +40,7 @@ export const initializeAxios = (): Promise => { export function setAuthorizationHeader(jwt: string): void { - ax.defaults.headers.common['Authorization'] = jwt; + ax.defaults.headers.common['Authorization'] = `Bearer ${jwt}`; } export function resetAuthorizationHeader(): void { diff --git a/client/app/components/content-container/content-container.tsx b/client/app/components/content-container/content-container.tsx index eaae5d4..bbafcae 100644 --- a/client/app/components/content-container/content-container.tsx +++ b/client/app/components/content-container/content-container.tsx @@ -4,6 +4,7 @@ import './content-container.scss'; interface Props { className?: string; + style?: any; } interface State {} @@ -15,7 +16,7 @@ export class ContentContainer extends React.Component { render() { return ( -
+
diff --git a/client/app/components/login-button/login-button.tsx b/client/app/components/login-button/login-button.tsx index ac8b55d..ad76c7e 100644 --- a/client/app/components/login-button/login-button.tsx +++ b/client/app/components/login-button/login-button.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import { RouteComponentProps } from 'react-router-dom'; import { inject, observer } from 'mobx-react'; +import { Portrait } from '../portrait/portrait'; import { UserStore } from '../../stores/user-store'; -interface Props { +interface Props extends RouteComponentProps { className?: string; userStore?: UserStore; } @@ -23,13 +25,28 @@ export class LoginButton extends React.Component { window.open(oauthUrl, '_blank', 'resizeable=yes, height=900, width=1200'); } - render() { + renderPortrait() { return ( -
+
this.props.history.push('/user-account')} style={{ cursor: 'pointer' }}> + +
+ ); + } + + renderLoginButton() { + return ( +
); } + render() { + return ( +
+ {this.props.userStore!.user ? this.renderPortrait() : this.renderLoginButton()} +
+ ); + } } diff --git a/client/app/components/portrait/portrait.tsx b/client/app/components/portrait/portrait.tsx index 453bb08..ac3a27e 100644 --- a/client/app/components/portrait/portrait.tsx +++ b/client/app/components/portrait/portrait.tsx @@ -3,6 +3,7 @@ import './portrait.scss'; interface Props { imageSrc: any; + style?: any; } interface State { @@ -14,7 +15,7 @@ export class Portrait extends React.Component { render() { return ( -
+
diff --git a/client/app/model/avatar.ts b/client/app/model/avatar.ts new file mode 100644 index 0000000..2cbb388 --- /dev/null +++ b/client/app/model/avatar.ts @@ -0,0 +1,71 @@ +export interface AvatarModel { + title: string; + imageSrc: any; +} + +export const AvatarList: AvatarModel[] = [ + { + title: 'dwarf_f', + imageSrc: require('../assets/avatars/Dwarf_female.gif'), + }, + { + title: 'dwarf_m', + imageSrc: require('../assets/avatars/Dwarf_male.gif'), + }, + { + title: 'gnome_f', + imageSrc: require('../assets/avatars/Gnome_female.gif'), + }, + { + title: 'gnome_m', + imageSrc: require('../assets/avatars/Gnome_male.gif'), + }, + { + title: 'human_f', + imageSrc: require('../assets/avatars/Human_female.gif'), + }, + { + title: 'human_m', + imageSrc: require('../assets/avatars/Human_male.gif'), + }, + { + title: 'night_elf_f', + imageSrc: require('../assets/avatars/Night_elf_female.gif'), + }, + { + title: 'night_elf_m', + imageSrc: require('../assets/avatars/Night_elf_male.gif'), + }, + { + title: 'orc_f', + imageSrc: require('../assets/avatars/Orc_female.gif'), + }, + { + title: 'orc_m', + imageSrc: require('../assets/avatars/Orc_male.gif'), + }, + { + title: 'tauren_f', + imageSrc: require('../assets/avatars/Tauren_female.gif'), + }, + { + title: 'tauren_m', + imageSrc: require('../assets/avatars/Tauren_male.gif'), + }, + { + title: 'troll_f', + imageSrc: require('../assets/avatars/Troll_female.gif'), + }, + { + title: 'troll_m', + imageSrc: require('../assets/avatars/Troll_male.gif'), + }, + { + title: 'undead_f', + imageSrc: require('../assets/avatars/Undead_female.gif'), + }, + { + title: 'undead_m', + imageSrc: require('../assets/avatars/Undead_male.gif'), + }, +]; diff --git a/client/app/model/character.ts b/client/app/model/character.ts new file mode 100644 index 0000000..b55be31 --- /dev/null +++ b/client/app/model/character.ts @@ -0,0 +1,13 @@ +export interface CharacterModel { + achievementPoints: number; + battlegroup: string; + class: number; + gender: number; + guild: string; + lastModified: number; + level: number; + name: string; + race: number; + realm: string; + spec: any; +} diff --git a/client/app/model/index.ts b/client/app/model/index.ts index 49d7b20..97ce127 100644 --- a/client/app/model/index.ts +++ b/client/app/model/index.ts @@ -1,4 +1,6 @@ +export * from './avatar'; export * from './category'; +export * from './character'; export * from './reply'; export * from './thread'; export * from './user'; diff --git a/client/app/model/user.ts b/client/app/model/user.ts index db5f342..3565d98 100644 --- a/client/app/model/user.ts +++ b/client/app/model/user.ts @@ -5,4 +5,9 @@ export interface UserModel { id: number; permissions: string; token: string; + character_name?: string; + character_class?: string; + character_guild?: string; + character_avatar?: string; + character_realm?: string; } diff --git a/client/app/pages/forum/forum.tsx b/client/app/pages/forum/forum.tsx index 197d832..04e9ca6 100644 --- a/client/app/pages/forum/forum.tsx +++ b/client/app/pages/forum/forum.tsx @@ -34,7 +34,7 @@ export class Forum extends React.Component {
- +
); diff --git a/client/app/pages/user-account/user-account.scss b/client/app/pages/user-account/user-account.scss new file mode 100644 index 0000000..e110247 --- /dev/null +++ b/client/app/pages/user-account/user-account.scss @@ -0,0 +1,20 @@ +.avatar-list { + display: flex; + flex-wrap: wrap; + padding: 20px; + padding-left: 10px; + + &__item { + margin-left: 10px; + margin-bottom: 10px; + cursor: pointer; + position: relative; + transition: all 0.1s ease-in-out; + top: 0; + + &:hover, &__selected { + top: -1px; + box-shadow: 0 0 10px #00C0FF; + } + } +} diff --git a/client/app/pages/user-account/user-account.tsx b/client/app/pages/user-account/user-account.tsx index 949d5c9..50b677d 100644 --- a/client/app/pages/user-account/user-account.tsx +++ b/client/app/pages/user-account/user-account.tsx @@ -1,18 +1,135 @@ import React from 'react'; +import { inject, observer } from 'mobx-react'; import { RouteComponentProps } from 'react-router-dom'; -import { ContentContainer } from '../../components'; +import { get, groupBy, map } from 'lodash'; +import { ContentContainer, Portrait, ScrollToTop } from '../../components'; +import { UserStore } from '../../stores/user-store'; +import { UserService } from '../../services'; +import { AvatarList, CharacterModel } from '../../model'; +import './user-account.scss'; -interface Props extends RouteComponentProps {} +interface Props extends RouteComponentProps { + userStore?: UserStore; +} -interface State {} +interface State { + characters: {[realm: string]: CharacterModel[]}; + selectedRealm?: string; + selectedCharIndex: number; +} +@inject('userStore') +@observer export class UserAccount extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + characters: {}, + selectedCharIndex: 0, + }; + } - componentDidMount() {} + componentDidMount() { + this.getCharacters(); + } + + private selectedCharacter(): CharacterModel { + const { selectedRealm, selectedCharIndex } = this.state; + const char = get(this.state, `characters[${selectedRealm}][${selectedCharIndex}]`); + return char; + } + + async getCharacters() { + try { + const res = await UserService.getCharacters() as any; + const characters = groupBy(res, 'realm'); + this.setState({ + characters, + selectedRealm: res[0].realm, + }); + } catch (e) { + console.error(e); + } + } + + onRealmSelect(event: any) { + this.setState({ selectedRealm: event.target.value, selectedCharIndex: 0 }); + } + + onCharSelect(event: any) { + this.setState({ selectedCharIndex: event.target.value as any }); + } + + renderDropDowns() { + if (!this.selectedCharacter()) { + return
; + } + return ( +
+ + + +
+ {AvatarList.map((val, index) => { + return ( +
+ +
+ ); + })} +
+
+ ); + } + + private async onSave() { + const { name, guild, realm } = this.selectedCharacter(); + const data = { + character_name: name, + character_class: 'Rogue', // todo get class from number + character_guild: guild, + character_realm: realm, + character_avatar: 'Avatar', // TODO: + }; + + await UserService.saveCharacter(data); + } render() { + const { battletag, character_name, character_class, character_guild, character_realm } = this.props.userStore!.user!; + return ( - + + +
+ +
+ {battletag &&
Battletag: {battletag}
} + {character_name &&
Character: {character_name}
} + {character_class &&
Class: {character_class}
} + {character_guild &&
Guild: {character_guild}
} + {character_realm &&
Realm: {character_realm}
} +
+
+ +
+

Set a new default character

+ {this.renderDropDowns()} + this.onSave()}>Save +
+ +
+
); } } diff --git a/client/app/services/user.service.ts b/client/app/services/user.service.ts index c6c3aea..0162014 100644 --- a/client/app/services/user.service.ts +++ b/client/app/services/user.service.ts @@ -1,16 +1,41 @@ import axios from '../axios/axios'; import userStore from '../stores/user-store'; +import { CharacterModel } from '../model'; // fetch user and store in local storage const authorize = async (code: string): Promise => { try { - const res = await axios.post('/api/battlenet/authorize', { code }); + const res = await axios.post('/api/user/authorize', { code }); userStore.setUser(res.data.data); } catch (e) { console.error(e); } }; + +const getCharacters = async (): Promise => { + try { + const res = await axios.get('/api/user/characters'); + return res.data.data.characters; + } catch (e) { + console.error(e); + } + return null; +}; + +const saveCharacter = async (character: any): Promise => { + try { + const res = await axios.put('/api/user/characters', character); + const user = res.data.data; + userStore.setCharacterInfo(user); + return user; + } catch (e) { + console.error(e); + } + return null; +}; export const UserService = { authorize, + saveCharacter, + getCharacters, }; diff --git a/client/app/stores/user-store.ts b/client/app/stores/user-store.ts index 7e25d1d..58b7181 100644 --- a/client/app/stores/user-store.ts +++ b/client/app/stores/user-store.ts @@ -13,11 +13,6 @@ export class UserStore { }); } - @action setUser(user: UserModel) { - localStorage.setItem('user', JSON.stringify(user)); - this.getUserFromStorage(); - } - @action private getUserFromStorage(): void { const u = localStorage.getItem('user'); if (u) { @@ -25,6 +20,23 @@ export class UserStore { } } + @action setUser(user: UserModel) { + localStorage.setItem('user', JSON.stringify(user)); + this.getUserFromStorage(); + } + + @action public setCharacterInfo(info: {[key: string]: string}) { + const { character_avatar, character_class, character_guild, character_name, character_realm } = info; + const user = {...this.user!, + character_avatar, + character_class, + character_guild, + character_name, + character_realm, + }; + this.setUser(user); + } + // when the user logs out @action resetUser() { this.user = undefined; diff --git a/lib/myapp/data/user.ex b/lib/myapp/data/user.ex index cd8251e..a1fa150 100644 --- a/lib/myapp/data/user.ex +++ b/lib/myapp/data/user.ex @@ -48,7 +48,7 @@ defmodule MyApp.Data.User do defp get_user(battle_net_id) do query = from u in "user", where: u.battle_net_id == ^battle_net_id, - select: [:id, :permissions, :battle_net_id, :battletag] + select: [:id, :permissions, :battle_net_id, :battletag, :character_guild, :character_name, :character_class, :character_realm, :character_avatar] Repo.one(query) end