rendering profile in sidebar

This commit is contained in:
Roland Osborne 2022-03-18 10:51:21 -07:00
parent 68c6acaf64
commit 1d3d1a44a8
14 changed files with 231 additions and 18 deletions

View File

@ -11,7 +11,7 @@ import (
func GetProfileImage(w http.ResponseWriter, r *http.Request) {
var data []byte
account, code, err := BearerAppToken(r, true);
account, code, err := ParamAppToken(r, true);
if err != nil {
ErrResponse(w, code, err)
return

View File

@ -77,6 +77,40 @@ func BearerAccountToken(r *http.Request) (*store.AccountToken, error) {
return &accountToken, nil
}
func ParamAppToken(r *http.Request, detail bool) (*store.Account, int, error) {
// parse authentication token
target, access, err := ParseToken(r.FormValue("token"))
if err != nil {
return nil, http.StatusBadRequest, err
}
// find token record
var app store.App
if detail {
if err := store.DB.Preload("Account.AccountDetail").Where("account_id = ? AND token = ?", target, access).First(&app).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, http.StatusNotFound, err
} else {
return nil, http.StatusInternalServerError, err
}
}
} else {
if err := store.DB.Preload("Account").Where("account_id = ? AND token = ?", target, access).First(&app).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, http.StatusNotFound, err
} else {
return nil, http.StatusInternalServerError, err
}
}
}
if app.Account.Disabled {
return nil, http.StatusGone, errors.New("account is inactive")
}
return &app.Account, http.StatusOK, nil
}
func BearerAppToken(r *http.Request, detail bool) (*store.Account, int, error) {
// parse bearer authentication

View File

@ -9,6 +9,7 @@ func getProfileModel(account *store.Account) *Profile {
return &Profile{
Guid: account.Guid,
Handle: account.Username,
Name: account.AccountDetail.Name,
Description: account.AccountDetail.Description,
Location: account.AccountDetail.Location,
Image: account.AccountDetail.Image,

View File

@ -102,7 +102,7 @@ func TestProfileUpdate(t *testing.T) {
APP_TOKENAPP, set.A.Token, &profile, nil))
// retrieve profile image
data, hdr, err = ApiTestData(GetProfileImage, "GET", "/profile/image", nil, nil,
data, hdr, err = ApiTestData(GetProfileImage, "GET", "/profile/image?token=" + set.A.Token, nil, nil,
APP_TOKENAPP, set.A.Token, 0, 0)
assert.NoError(t, err)

View File

@ -7,6 +7,12 @@ export function Login(props) {
const { state, actions } = useLogin()
const keyDown = (e) => {
if (e.key === 'Enter') {
actions.onLogin()
}
}
return(
<LoginWrapper>
<div class="container">
@ -15,9 +21,9 @@ export function Login(props) {
<span class="subheader-text">Communication for the Decentralized Web</span>
</div>
<LoginInput size="large" spellCheck="false" placeholder="username" prefix={<UserOutlined />}
onChange={(e) => actions.setUsername(e.target.value)} value={state.username} />
onChange={(e) => actions.setUsername(e.target.value)} value={state.username} onKeyDown={(e) => keyDown(e)}/>
<LoginPassword size="large" spellCheck="false" placeholder="password" prefix={<LockOutlined />}
onChange={(e) => actions.setPassword(e.target.value)} value={state.password} />
onChange={(e) => actions.setPassword(e.target.value)} value={state.password} onKeyDown={(e) => keyDown(e)}/>
<LoginEnter type="primary" onClick={() => actions.onLogin()} disabled={actions.isDisabled()}>
<span>Sign In</span>
</LoginEnter>

View File

@ -28,7 +28,7 @@ export function useLogin() {
return false
},
onLogin: async () => {
if (!state.spinning) {
if (!state.spinning && state.username != '' && state.password != '') {
actions.updateState({ spinning: true })
try {
await app.actions.login(state.username, state.password)

View File

@ -0,0 +1,48 @@
import { Avatar, Image } from 'antd';
import React from 'react'
import { IdentityWrapper } from './Identity.styled';
import { DownOutlined, EditOutlined, UserOutlined } from '@ant-design/icons';
import { useIdentity } from './useIdentity.hook';
import { Menu, Dropdown } from 'antd';
export function Identity() {
const { state, actions } = useIdentity()
const Logo = () => {
if (state.imageUrl === '') {
return <Avatar size={64} icon={<UserOutlined />} />
}
return <Avatar size={64} src={<Image preview={false} src={ state.imageUrl } style={{ width: 64 }} />} />
}
const menu = (
<Menu>
<Menu.Item key="0">
<div onClick={() => {}}>Edit Profile</div>
</Menu.Item>
<Menu.Item key="1">
<div onClick={() => actions.logout()}>Sign Out</div>
</Menu.Item>
</Menu>
);
return (
<IdentityWrapper>
<Dropdown overlay={menu} overlayStyle={{ minWidth: 0 }} trigger={['click']} placement="bottomRight">
<div>
<div class="container">
<div class="logo">
<Logo />
</div>
<div class="username">
<span class="name">{ state.name }</span>
<span class="handle">{ state.handle }</span>
</div>
<DownOutlined />
</div>
</div>
</Dropdown>
</IdentityWrapper>
)
}

View File

@ -0,0 +1,68 @@
import { Button } from 'antd';
import styled from 'styled-components';
export const IdentityWrapper = styled.div`
background-color: #f6f5ed;
border-bottom: 2px solid #8fbea7;
border-top: 1px solid #8fbea7;
border-left: 0px;
border-right: 0px;
border-radius: 0px;
&:hover {
cursor: pointer;
}
.container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding-left: 16px;
padding-right: 16px;
}
.menu {
min-width: 0px;
}
.logo {
width: 33%
display: flex;
align-items: center;
justify-content: center;
padding-top: 4px;
padding-bottom: 4px;
}
.username {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-right: 8px;
}
.edit {
position: absolute;
display: flex;
align-items: flex-end;
right: 0;
bottom: 0;
}
.name {
font-size: 1.25em;
color: #444444;
}
.handle {
font-size: 1em;
color: #444444;
}
.domain {
font-size: 1em;
color: #444444;
}
`;

View File

@ -0,0 +1,42 @@
import { useContext, useState, useEffect } from 'react';
import { AppContext } from '../../../AppContext/AppContext';
export function useIdentity() {
const [state, setState] = useState({
name: '',
handle: '',
domain: '',
imageUrl: ''
});
const actions = {
logout: async () => {
app.actions.logout()
}
};
const app = useContext(AppContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
if (app?.state?.Data?.profile) {
let profile = app.state.Data.profile;
if (profile.name != null) {
updateState({ name: profile.name });
}
if (profile.image != null) {
updateState({ imageUrl: 'https://' + profile.node + '/profile/image?token=' + app.state.token })
} else {
updateState({ imageUrl: '' })
}
updateState({ handle: profile.handle });
updateState({ domain: profile.node });
}
}, [app])
return { state, actions };
}

View File

@ -0,0 +1,12 @@
import React from 'react'
import { SideBarWrapper } from './SideBar.styled';
import { Identity } from './Identity/Identity';
export function SideBar() {
return (
<SideBarWrapper>
<Identity />
</SideBarWrapper>
)
}

View File

@ -0,0 +1,11 @@
import styled from 'styled-components';
export const SideBarWrapper = styled.div`
width: 30%;
height: 100%;
max-width: 300px;
min-width: 200px;
border: 1px solid #8fbea7;
display: flex;
flex-direction: column;
`;

View File

@ -2,6 +2,7 @@ import React from 'react'
import { useUser } from './useUser.hook';
import { Button } from 'antd';
import { UserWrapper } from './User.styled';
import { SideBar } from './SideBar/SideBar';
import connect from '../connect.png';
@ -11,11 +12,10 @@ export function User() {
return (
<UserWrapper>
<div class="listing">
<Button type="primary" onClick={() => actions.onLogout()} style={{ alignSelf: 'center', marginTop: '16px', width: '33%' }}>Sign Out</Button>
</div>
<SideBar />
<div class="canvas">
<img class="connect" src={connect} alt="" />
<Button type="primary" onClick={() => actions.onLogout()} style={{ alignSelf: 'center', marginTop: '16px', width: '33%' }}>Sign Out</Button>
</div>
</UserWrapper>
)

View File

@ -8,16 +8,9 @@ export const UserWrapper = styled.div`
height: 100%;
background-color: #f6f5ed;
.listing {
width: 30%;
height: 100%;
max-width: 300px;
min-width: 200px;
border: 1px solid #8fbea7;
}
.canvas {
display: flex;
flex-direction: column;
flex-grow: 1;
height: 100%;
border: 1px solid #8fbea7;

View File

@ -9,8 +9,6 @@ export function useUser() {
const navigate = useNavigate();
const app = useContext(AppContext);
console.log(app);
const actions = {
onLogout: async () => {
app.actions.logout()