Rework user page, more page concepts.

- Added page concept for games.

- User page now mostly resembles what Roblox's user profile page looked like back in 2016.

- Added biography for users.
This commit is contained in:
Graphictoria 2022-10-19 23:57:39 -04:00
parent 550fafeee5
commit 6c482f5a22
12 changed files with 315 additions and 141 deletions

BIN
etc/art/gamebusy.pdn Normal file

Binary file not shown.

View File

@ -91,15 +91,12 @@ class User extends Authenticatable implements MustVerifyEmail
return $this->created_at->isoFormat('ll');
}
// XlXi: Returns a class name like text-success or
// gt-status-studio to show next to the
// user's name.
public function getOnlineClass()
// XlXi: TODO: Replace this with detailed presence
// like what game the user is in or
// what place they're editing.
public function isOnline()
{
if($this->last_seen >= Carbon::now()->subMinutes(2))
return 'text-success';
return 'text-muted';
return ($this->last_seen >= Carbon::now()->subMinutes(2));
}
public function _hasRolesetInternal($roleName)

View File

@ -22,6 +22,8 @@ return new class extends Migration
$table->rememberToken();
$table->unsignedBigInteger('banId')->nullable();
$table->string('biography')->nullable();
$table->unsignedBigInteger('tokens')->default(0);
$table->dateTime('next_reward')->useCurrent();

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,120 @@
/*
Graphictoria 5 (https://gtoria.net)
Copyright © XlXi 2022
*/
import { Component, createRef } from 'react';
import classNames from 'classnames/bind';
import axios from 'axios';
import Twemoji from 'react-twemoji';
import { buildGenericApiUrl } from '../util/HTTP.js';
import ProgressiveImage from './ProgressiveImage';
import Loader from './Loader';
axios.defaults.withCredentials = true;
function commaSeparate(num) {
let str = num.toString().split('.');
str[0] = str[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return str.join('.');
}
class GameItemCard extends Component {
constructor(props) {
super(props);
this.state = {
hovered: false
}
}
render() {
return (
<a
className="graphictoria-item-card"
href="#"
onMouseEnter={() => this.setState({hovered: true})}
onMouseLeave={() => this.setState({hovered: false})}
>
<span className="card m-2">
<ProgressiveImage
src={ buildGenericApiUrl('www', 'images/busy/game.png') }
placeholderImg={ buildGenericApiUrl('www', 'images/busy/game.png') }
alt="Game todo"
className='img-fluid'
/>
<div className="p-2">
<p>Todo</p>
<p className="text-muted small">{commaSeparate(1337)} Playing</p>
<div className="d-flex mt-1">
<i className={classNames({
'fa-solid': true,
'fa-thumbs-up': true,
'text-success': this.state.hovered
})}></i>
<div className={classNames({
'my-auto': true,
'mx-1': true,
'graphictoria-vote-bar': true,
'rounded-1': true,
'border': true,
'border-light': true,
'position-relative': true,
'flex-fill': true,
'bg-secondary': !this.state.hovered,
'bg-danger': this.state.hovered
})}>
<div className={classNames({
'rounded-1': true,
'position-absolute': true,
'bg-dark': !this.state.hovered,
'bg-success': this.state.hovered,
})}
style={{width: '80%', height: '8px'}}></div>
</div>
<i className={classNames({
'fa-solid': true,
'fa-thumbs-down': true,
'text-danger': this.state.hovered
})}></i>
</div>
</div>
</span>
{
this.state.hovered ?
<span className="graphictoria-item-details">
<div className="card px-2">
<hr className="m-0" />
<p className="text-truncate my-1">
<span className="text-muted">By </span><a href="#" className="text-decoration-none fw-normal">Todo</a>
</p>
</div>
</span>
:
null
}
</a>
);
}
}
class Games extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="container-lg my-2 d-flex flex-column">
<h4 className="my-auto">Games</h4>
<Loader />
<GameItemCard />
</div>
);
}
}
export default Games;

View File

@ -0,0 +1,19 @@
/*
Graphictoria 5 (https://gtoria.net)
Copyright © XlXi 2022
*/
import $ from 'jquery';
import React from 'react';
import { render } from 'react-dom';
import Games from '../components/Games';
const gamesId = 'gt-games-main';
$(document).ready(function() {
if (document.getElementById(gamesId)) {
render(<Games />, document.getElementById(gamesId));
}
});

View File

@ -1345,3 +1345,29 @@ p {
-webkit-animation-fill-mode: both;
-webkit-animation-name: dropdownEase;
}
// Voting
.graphictoria-vote-bar {
// border
html.gtoria-dark & {
border-color: $gray-700;
}
html.gtoria-light & {
border-color: $border-color;
}
// rounded-1
border-radius: 0.2rem;
// bg-secondary
background-color: $secondary;
// my-auto and mx-1
margin: auto 0.25rem auto 0.25rem;
// flex-fill
flex: 1 1 auto;
position: relative;
height: 10px;
}

View File

@ -2,6 +2,10 @@
@section('title', 'Games')
@section('page-specific')
<script src="{{ mix('js/Games.js') }}"></script>
@endsection
@section('content')
<div id="gt-games-main" class="container-lg my-2 d-flex flex-column">
<h4 class="my-auto">Games</h4>

View File

@ -8,39 +8,31 @@
@section('content')
<div class="container-lg my-2">
<h4>Hello, {{ Auth::user()->username }}!</h4>
<div class="row">
<div class="col-md-3">
<h4>Hello, {{ Auth::user()->username }}!</h4>
<div class="card text-center">
<img src="{{ asset('/images/testing/avatar.png') }}" class="img-fluid gt-charimg" />
</div>
<x-MiniCard class="mt-3 d-none d-md-flex">
<x-slot name="title">
Blog
</x-slot>
<x-slot name="body">
<ul class="text-center list-unstyled mb-1">
<li class="pb-2"><a href="#" class="text-decoration-none fw-normal"><i class="fa-solid fa-circle-right"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</a></li>
<li class="pb-2"><a href="#" class="text-decoration-none fw-normal"><i class="fa-solid fa-circle-right"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</a></li>
<li class="pb-2"><a href="#" class="text-decoration-none fw-normal"><i class="fa-solid fa-circle-right"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</a></li>
</ul>
<div class="text-left px-2">
<a href="https://blog.gtoria.net" class="text-decoration-none fw-normal" target="_blank">More <i class="fa-solid fa-caret-right"></i></a>
</div>
</x-slot>
</x-MiniCard>
<h4 class="mt-3">Blog</h4>
<div class="card p-2">
<ul class="text-center list-unstyled mb-0">
<li class="pb-2"><a href="#" class="text-decoration-none fw-normal"><i class="fa-solid fa-circle-right"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</a></li>
<li class="pb-2"><a href="#" class="text-decoration-none fw-normal"><i class="fa-solid fa-circle-right"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</a></li>
<li class="pb-2"><a href="#" class="text-decoration-none fw-normal"><i class="fa-solid fa-circle-right"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</a></li>
</ul>
<div class="text-left">
<a href="https://blog.gtoria.net" class="text-decoration-none fw-normal" target="_blank">More <i class="fa-solid fa-caret-right"></i></a>
</div>
</div>
</div>
<div class="col-md-9 mt-3 mt-md-0">
<x-MiniCard class="d-none d-md-flex mb-3">
<x-slot name="title">
Recently Played
</x-slot>
<x-slot name="body">
Content here.
</x-slot>
</x-MiniCard>
<div class="col-md-9">
<h4 class="mt-3 mt-md-0">Recently Played</h4>
<div class="card p-2 mb-3">
Content here.
</div>
<div id="gt-dash-feed"></div>
</div>

View File

@ -1,10 +1,11 @@
{{-- XlXi: References lol --}}
{{-- XlXi: https://cubash.com/@Icseon --}}
{{-- XlXi: https://youtu.be/nouY1ugddcI?t=583 --}}
@extends('layouts.app')
@section('title', $title)
@section('page-specific')
@endsection
@section('quick-admin')
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
@ -17,113 +18,100 @@
@endsection
@section('content')
<style>
.graphictoria-user-profile-buttons {
display: flex;
justify-content: center;
}
.graphictoria-user-profile-buttons > .btn:not(:last-child) {
/* me-1 */
margin-right: 0.25rem!important;
}
</style>
<div class="container-lg my-2">
<div class="graphictoria-smaller-page">
<div class="row">
<div class="col-md-6">
<div class="card p-2 text-center">
<h5 class="mb-0"><span class="{{ $user->getOnlineClass() }}"></span> {{ $user->username }}</h5>
@if(false)
{{-- TODO: XlXi: this lol --}}
<p class="text-success">[ Playing: Among Us ]</p>
@endif
<img src="{{ asset('/images/testing/avatar.png') }}" width="300px" height="300px" class="img-fluid mx-auto gt-charimg" />
<p class="text-muted pb-2">TODO: user description</p>
@auth
<div class="graphictoria-user-profile-buttons">
<button class="btn btn-secondary">Add Friend</button>
<button class="btn btn-secondary">Send Message</button>
<button class="btn btn-secondary">Block</button>
</div>
@endauth
<hr class="mb-1" />
<div class="row text-center">
<div class="col-4">
<p class="fw-bold">Joined</p>
<p>{{ $user->getJoinDate() }}</p>
</div>
<div class="col-4">
<p class="fw-bold">Last Seen</p>
<p>{{ $user->getLastSeen() }}</p>
</div>
<div class="col-4">
<p class="fw-bold">Visits</p>
<p>0</p>
</div>
</div>
<hr class="mt-1 mb-1" />
@auth
<a href="https://www.gtoria.local/todo123" class="ms-auto text-decoration-none link-danger">Report <i class="fa-solid fa-circle-exclamation"></i></a>
@endauth
</div>
<x-MiniCard class="mt-3 d-flex">
<x-slot name="title">
Official Badges
</x-slot>
<x-slot name="body">
<p class="text-muted">todo</p>
</x-slot>
</x-MiniCard>
<x-MiniCard class="mt-3 d-flex">
<x-slot name="title">
Badges
</x-slot>
<x-slot name="body">
<p class="text-muted">todo</p>
</x-slot>
</x-MiniCard>
<div class="container-lg my-4 graphictoria-smaller-page">
{{-- User pane --}}
<div class="card p-2">
<div class="d-flex">
<div class="pe-3">
<img class="img-fluid border graphictora-user-circle m-1" src="{{ asset('/images/testing/headshot.png') }}" alt="User avatar of {{ $user->username }}" width="120px" />
</div>
<div class="col-md-6">
<x-MiniCard class="mt-3 mt-md-0 d-flex">
<x-slot name="title">
Games
</x-slot>
<x-slot name="body">
<p class="text-muted">todo</p>
</x-slot>
</x-MiniCard>
<x-MiniCard class="mt-3 d-flex">
<x-slot name="title">
Favorites
</x-slot>
<x-slot name="body">
<p class="text-muted">todo</p>
</x-slot>
</x-MiniCard>
<x-MiniCard class="mt-3 d-flex">
<x-slot name="title">
Friends
</x-slot>
<x-slot name="body">
<p class="text-muted">todo</p>
</x-slot>
</x-MiniCard>
<div class="flex-fill d-flex flex-column p-2">
{{-- TODO: XlXi: Advanced presence --}}
<h4 class="mb-0">
{{ $user->username }}
<span @class([
'text-muted' => !$user->isOnline(),
'text-success' => $user->isOnline()
])>({{ $user->isOnline() ? 'Online' : 'Offline' }})</span>
</h4>
<p>"This is my current status!"</p>
<div class="d-md-flex mt-auto">
<a href="#" class="btn btn-primary ms-auto">Send Message</a>
<a href="#" class="btn btn-success ms-1">Add Friend</a>
</div>
</div>
</div>
</div>
{{-- About pane --}}
<h4 class="mt-2">About</h4>
<div class="card p-2">
@if($user->biography)
<p>{{ $user->biography }}</p>
@else
<i class="text-muted">This user has no description.</i>
@endif
<hr class="my-2" />
<div class="d-flex">
{{-- TODO: XlXi: convert this to a route --}}
<a href="https://www.gtoria.local/report/user/notfinishedtodo" target="_blank" class="text-decoration-none link-danger ms-auto">Report <i class="fa-solid fa-circle-exclamation"></i></a>
</div>
</div>
{{-- Games pane --}}
<h4 class="mt-2">Games</h4>
<div class="card p-3">
<div>
{{-- XlXi: just reuse the shop cards for this lol --}}
{{-- XlXi: https://cdn.discordapp.com/attachments/845538783592054795/1028135570335092826/unknown.png --}}
</div>
</div>
<div class="row">
<div class="col-md-6">
{{-- Badges pane --}}
<h4 class="mt-2">Badges</h4>
<div class="card p-2">
{{-- TODO: XlXi: badges --}}
</div>
</div>
<div class="col-md-6">
{{-- Friends pane --}}
<h4 class="mt-2">Friends</h4>
<div class="card p-2">
{{-- TODO: XlXi: friends --}}
{{-- TODO: XlXi: Sort friends by online --}}
</div>
</div>
</div>
{{-- Groups pane --}}
<h4 class="mt-2">Groups</h4>
<div class="card p-2">
</div>
{{-- Statistics pane --}}
<h4 class="mt-2">Statistics</h4>
<div class="card p-3">
<div class="row text-center">
<div class="col">
<p class="fw-bold">Joined</p>
<p>{{ $user->getJoinDate() }}</p>
</div>
<div class="col">
<p class="fw-bold">Last Seen</p>
<p>{{ $user->getLastSeen() }}</p>
</div>
<div class="col">
<p class="fw-bold">Visits</p>
<p>todo</p>
</div>
<div class="col">
<p class="fw-bold">Forum Posts</p>
<p>todo</p>
</div>
</div>
<x-MiniCard class="mt-3 d-flex">
<x-slot name="title">
Groups
</x-slot>
<x-slot name="body">
<p class="text-muted">todo</p>
</x-slot>
</x-MiniCard>
</div>
</div>
@endsection

View File

@ -8,6 +8,31 @@
@section('content')
<div id="gt-settings-main" class="container mx-auto my-2">
<h4 class="my-auto">Settings</h4>
<x-loader />
<ul class="nav nav-tabs">
<li class="nav-item">
<button class="nav-link active">Account</button>
</li>
<li class="nav-item">
<button class="nav-link">Security</button>
</li>
<li class="nav-item">
<button class="nav-link">Privacy</button>
</li>
<li class="nav-item">
<button class="nav-link">Appearance</button>
</li>
</ul>
<div class="card p-2">
<h5>Account Settings</h5>
<div class="row">
<div class="col-3 fw-bold">
<p>Username</p>
</div>
<div class="col-9">
<p>XlXi</p>
</div>
</div>
</div>
</div>
@endsection

View File

@ -11,6 +11,7 @@ mix.js('resources/js/app.js', 'public/js')
.js('resources/js/pages/Maintenance.js', 'public/js')
.js('resources/js/pages/Dashboard.js', 'public/js')
.js('resources/js/pages/Shop.js', 'public/js')
.js('resources/js/pages/Games.js', 'public/js')
.js('resources/js/pages/Item.js', 'public/js')
.js('resources/js/pages/SiteConfiguration.js', 'public/js/adm')
.react()