This commit is contained in:
copyrighttxt
2026-07-06 13:28:00 -04:00
commit 05211f73ef
441 changed files with 94128 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
{% extends '__layout__.html' %}
{% block title %}{{profile.username}}{% endblock %}
{% block head %}
<link href="/static/css/profile.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
<style>
.text-secondary {
color: rgb(199, 199, 199) !important;
}
.border {
border-color: rgb(60,60,60) !important;
}
.nav-link {
color: rgb(199, 199, 199) !important;
}
.nav-link.active {
color: rgb(255, 255, 255) !important;
}
</style>
{% endblock %}
{% block content %}
<div id="main">
<div class="container" style="max-width: 800px;">
<div class="d-flex align-items-center">
<a href="/users/{{profile.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{profile.id}}" width="80px" height="80px" class="rounded-2">
</a>
<h2 class="ms-3">{{profile.username}}'s Followers</h2>
</div>
<nav class="mt-2 rounded-top" style="background-color: rgb(40, 40, 40) !important;overflow: hidden;">
<div class="nav nav-underline nav-fill" id="nav-tab" role="tablist">
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/friends">Friends</a>
<a class="nav-link" type="button" role="tab" aria-selected="true" href="/users/{{profile.id}}/following">Following</a>
<a class="nav-link active" type="button" role="tab" aria-selected="true">Followers</a>
{% if profile.id == currentuser.id %}
<a class="nav-link" type="button" role="tab" aria-selected="true" href="/users/{{profile.id}}/requests">Requests{% if currentuser.friend_requests > 0: %}<span class="badge text-bg-danger ms-1">{{currentuser.friend_requests}}</span>{%endif%}</a>
{%endif%}
</div>
</nav>
<div>
<h4 class="m-0 mt-2">Followers ( {{FollowCount}} )</h4>
<div class="row mt-2">
{% for following in FollowUsers: %}
<div class="col-6 col-sm-4 mb-2">
<div class="bg-dark border p-2 rounded d-flex">
<a href="/users/{{following.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{following.id}}" width="80px" height="80px" class="rounded-2" alt="{{following.username}}">
</a>
<a class="text-white text-decoration-none ms-2" href="/users/{{following.id}}/profile">{{following.username}}</a>
</div>
</div>
{%endfor%}
</div>
{% if len(FollowUsers) <= 0: %}
<p class="w-100 text-secondary text-center text-white mt-4 mb-4">No results found</p>
{% endif %}
</div>
<div class="d-flex mt-2 align-items-center">
<a class="ms-auto text-decoration-none {%if not isTherePreviousPage:%}text-secondary{%endif%}" {%if isTherePreviousPage: %}href="/users/{{profile.id}}/followers?page={{PageNumber - 1}}{%endif%}">Previous</a>
<p class="m-0 ms-2 me-2 text-white">Page {{PageNumber}} of {{TotalPages}}</p>
<a class="me-auto text-decoration-none {%if not isThereNextPage: %}text-secondary{%endif%}" {%if isThereNextPage: %}href="/users/{{profile.id}}/followers?page={{PageNumber + 1}}{%endif%}">Next</a>
</div>
</div>
</div>
{% endblock %}
+64
View File
@@ -0,0 +1,64 @@
{% extends '__layout__.html' %}
{% block title %}{{profile.username}}{% endblock %}
{% block head %}
<link href="/static/css/profile.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
<style>
.text-secondary {
color: rgb(199, 199, 199) !important;
}
.border {
border-color: rgb(60,60,60) !important;
}
.nav-link {
color: rgb(199, 199, 199) !important;
}
.nav-link.active {
color: rgb(255, 255, 255) !important;
}
</style>
{% endblock %}
{% block content %}
<div id="main">
<div class="container" style="max-width: 800px;">
<div class="d-flex align-items-center">
<a href="/users/{{profile.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{profile.id}}" width="80px" height="80px" class="rounded-2">
</a>
<h2 class="ms-3">{{profile.username}}'s Following</h2>
</div>
<nav class="mt-2 rounded-top" style="background-color: rgb(40, 40, 40) !important;overflow: hidden;">
<div class="nav nav-underline nav-fill" id="nav-tab" role="tablist">
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/friends">Friends</a>
<a class="nav-link active" type="button" role="tab" aria-selected="true">Following</a>
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/followers">Followers</a>
{% if profile.id == currentuser.id %}
<a class="nav-link" type="button" role="tab" aria-selected="true" href="/users/{{profile.id}}/requests">Requests{% if currentuser.friend_requests > 0: %}<span class="badge text-bg-danger ms-1">{{currentuser.friend_requests}}</span>{%endif%}</a>
{%endif%}
</div>
</nav>
<div>
<h4 class="m-0 mt-2">Following ( {{FollowCount}} )</h4>
<div class="row mt-2">
{% for following in FollowUsers: %}
<div class="col-6 col-sm-4 mb-2">
<div class="bg-dark border p-2 rounded d-flex">
<a href="/users/{{following.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{following.id}}" width="80px" height="80px" class="rounded-2" alt="{{following.username}}">
</a>
<a class="text-white text-decoration-none ms-2" href="/users/{{following.id}}/profile">{{following.username}}</a>
</div>
</div>
{%endfor%}
</div>
{% if len(FollowUsers) <= 0: %}
<p class="w-100 text-secondary text-center text-white mt-4 mb-4">No results found</p>
{% endif %}
</div>
<div class="d-flex mt-2 align-items-center">
<a class="ms-auto text-decoration-none {%if not isTherePreviousPage:%}text-secondary{%endif%}" {%if isTherePreviousPage: %}href="/users/{{profile.id}}/following?page={{PageNumber - 1}}{%endif%}">Previous</a>
<p class="m-0 ms-2 me-2 text-white">Page {{PageNumber}} of {{TotalPages}}</p>
<a class="me-auto text-decoration-none {%if not isThereNextPage: %}text-secondary{%endif%}" {%if isThereNextPage: %}href="/users/{{profile.id}}/following?page={{PageNumber + 1}}{%endif%}">Next</a>
</div>
</div>
</div>
{% endblock %}
+74
View File
@@ -0,0 +1,74 @@
{% extends '__layout__.html' %}
{% block title %}{{profile.username}}{% endblock %}
{% block head %}
<link href="/static/css/profile.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
<style>
.text-secondary {
color: rgb(199, 199, 199) !important;
}
.border {
border-color: rgb(60,60,60) !important;
}
.nav-link {
color: rgb(199, 199, 199) !important;
}
.nav-link.active {
color: rgb(255, 255, 255) !important;
}
</style>
{% endblock %}
{% block content %}
<div id="main">
<div class="container" style="max-width: 800px;">
<div class="d-flex align-items-center">
<a href="/users/{{profile.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{profile.id}}" width="80px" height="80px" class="rounded-2">
</a>
<h2 class="ms-3">{{profile.username}}'s Friends</h2>
</div>
<nav class="mt-2 rounded-top" style="background-color: rgb(40, 40, 40) !important;overflow: hidden;">
<div class="nav nav-underline nav-fill" id="nav-tab" role="tablist">
<a class="nav-link active" type="button" role="tab" aria-selected="true">Friends</a>
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/following">Following</a>
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/followers">Followers</a>
{% if profile.id == currentuser.id %}
<a class="nav-link" type="button" role="tab" aria-selected="true" href="/users/{{profile.id}}/requests">Requests{% if currentuser.friend_requests > 0: %}<span class="badge text-bg-danger ms-1">{{currentuser.friend_requests}}</span>{%endif%}</a>
{%endif%}
</div>
</nav>
<div>
<h4 class="m-0 mt-2">Friends ( {{FriendCount}} )</h4>
<div class="row mt-2">
{% for friend in FriendUsers: %}
<div class="col-6 col-sm-4 mb-2">
<div class="bg-dark border p-2 rounded d-flex position-relative">
<a href="/users/{{friend.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{friend.id}}" width="80px" height="80px" class="rounded-2" alt="{{friend.username}}">
</a>
<a class="text-white text-decoration-none ms-2" style="font-size: 14px;" href="/users/{{friend.id}}/profile">{{friend.username}}</a>
{% if currentuser.id == profile.id %}
<div class="dropdown d-flex justify-content-end position-absolute" style="top: 0; right: 0;">
<button class="btn" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end p-1">
<li><form action="/users/{{friend.id}}/unfriend?redirect=0" method="post"><input type="hidden" name="csrf_token" value="{{csrf_token()}}"><button class="dropdown-item" type="submit">Unfriend</button></form></li>
</ul>
</div>
{%endif%}
</div>
</div>
{%endfor%}
</div>
{% if len(FriendUsers) <= 0: %}
<p class="w-100 text-secondary text-center text-white mt-4 mb-4">No results found</p>
{% endif %}
</div>
<div class="d-flex mt-2 align-items-center">
<a class="ms-auto text-decoration-none {%if not isTherePreviousPage:%}text-secondary{%endif%}" {%if isTherePreviousPage: %}href="/users/{{profile.id}}/friends?page={{PageNumber - 1}}{%endif%}">Previous</a>
<p class="m-0 ms-2 me-2 text-white">Page {{PageNumber}} of {{TotalPages}}</p>
<a class="me-auto text-decoration-none {%if not isThereNextPage: %}text-secondary{%endif%}" {%if isThereNextPage: %}href="/users/{{profile.id}}/friends?page={{PageNumber + 1}}{%endif%}">Next</a>
</div>
</div>
</div>
{% endblock %}
+183
View File
@@ -0,0 +1,183 @@
{% extends '__layout__.html' %}
{% block title %}{{profile.username}} - Inventory{% endblock %}
{% block head %}
<link href="/static/css/profile.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
<style>
.text-secondary {
color: rgb(199, 199, 199) !important;
}
.category-list a {
background-color: rgb(31,31,31);
color: rgb(200, 200, 200);
padding: 8px;
transition: 0.1s;
box-shadow: 0px 0px 10px 0px rgb(0,0,0,0.5);
margin-top: 6px;
margin-bottom: 6px;
text-decoration: none;
width: 100%;
display: block;
border-left: 0px solid;
}
.category-list a:hover {
border-left: 5px solid rgb(160, 160, 160);
transition: 0.1s;
color: rgb(255,255,255);
}
.category-list a.selected {
border-left: 5px solid rgb(255,255,255);
transition: 0.1s;
color: rgb(255,255,255);
}
.item-results a {
text-decoration: none !important;
color: inherit;
}
.text-tickets {
color: rgb(224, 224, 60) !important;
font-size: small;
font-weight: 600;
}
.text-robux {
color: rgb(26, 212, 103) !important;
font-size: small;
font-weight: 600;
}
.text-limited {
color: rgb(255,255,255) !important;
background-color: rgb(9, 136, 62);
width: fit-content;
padding-left: 10px;
font-size: 10px;
border-top-right-radius: 5px;
margin: 0;
}
.text-limitedu {
color: rgb(0,0,0) !important;
background-color: rgb(245, 245, 52);
padding-right: 3px;
font-weight: 800;
border-top-right-radius: 5px;
padding-left: 3px;
}
.text-remaining {
color: rgb(197, 21, 21) !important;
font-size: 10px;
font-weight: 600;
}
</style>
{% endblock %}
{% block content %}
<div id="main">
<div class="container" style="max-width: 1200px;min-width: 800px;">
<div class="d-flex align-items-center">
<a href="/users/{{profile.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{profile.id}}" width="80px" height="80px" class="rounded-2">
</a>
<h2 class="ms-3">{{profile.username}}'s Inventory</h2>
</div>
<div class="d-flex">
<div class="p-1 category-list" style="width: 20%;">
<a href="/users/{{profile.id}}/inventory?category=0" class="{% if CategoryIndex == 0 %}selected{%endif%}">
Accessories
</a>
<a href="/users/{{profile.id}}/inventory?category=1" class="{% if CategoryIndex == 1 %}selected{%endif%}">
Audio
</a>
<a href="/users/{{profile.id}}/inventory?category=10" class="{% if CategoryIndex == 10 %}selected{%endif%}">
Gears
</a>
<a href="/users/{{profile.id}}/inventory?category=3" class="{% if CategoryIndex == 3 %}selected{%endif%}">
Faces
</a>
<a href="/users/{{profile.id}}/inventory?category=4" class="{% if CategoryIndex == 4 %}selected{%endif%}">
Heads
</a>
<a href="/users/{{profile.id}}/inventory?category=5" class="{% if CategoryIndex == 5 %}selected{%endif%}">
Hats
</a>
<a href="/users/{{profile.id}}/inventory?category=6" class="{% if CategoryIndex == 6 %}selected{%endif%}">
Passes
</a>
<a href="/users/{{profile.id}}/inventory?category=2" class="{% if CategoryIndex == 2 %}selected{%endif%}">
Packages
</a>
<a href="/users/{{profile.id}}/inventory?category=7" class="{% if CategoryIndex == 7 %}selected{%endif%}">
T-Shirt
</a>
<a href="/users/{{profile.id}}/inventory?category=8" class="{% if CategoryIndex == 8 %}selected{%endif%}">
Shirts
</a>
<a href="/users/{{profile.id}}/inventory?category=9" class="{% if CategoryIndex == 9 %}selected{%endif%}">
Pants
</a>
<a href="/users/{{profile.id}}/inventory?category=11" class="{% if CategoryIndex == 11 %}selected{%endif%}">
Badges
</a>
</div>
<div class="p-1 ps-3" style="width: 80%;">
<h3 class="m-0 mb-1">{{CategoryName}}</h3>
<div class="row item-results">
{% for item in SearchQuery %}
<div class="col-md-2 col-3 p-1">
<a href="{% if CategoryIndex != 11 %}/catalog/{{item.asset.id}}/{%else%}/badges/{{item.badge_id}}/{%endif%}">
<div class="rounded item-card p-2">
<div class="border rounded overflow-hidden position-relative">
<img src="/Thumbs/Asset.ashx?x=180&y=180&assetId={% if CategoryIndex != 11 %}{{item.asset.id}}{%else%}{{item.badge.icon_image_id}}{%endif%}" width="100%" style="aspect-ratio: 1/1;" alt="{% if CategoryIndex != 11 %}{{item.asset.name}}{%else%}{{item.badge.name}}{%endif%}">
{% if CategoryIndex != 11 %}
{% if item.asset.is_limited: %}<p class="position-absolute m-0 fw-bold text-limited" style="bottom:-1px;left:0px;">LIMITED {%if item.asset.is_limited_unique: %}<span class="text-limitedu">U</span>{%else%}<span class="me-2"></span>{%endif%}</p>{% endif %}
{% if item.asset.offsale_at != None %}<p class="position-absolute bg-danger m-0" style="top: 0px;right: 0px;border-bottom-left-radius: 10px;padding: 5px;"><i class="bi bi-alarm text-white"></i></p>{% endif %}
{% if item.serial != None %}<p class="position-absolute m-0 rounded p-1 fw-bold bg-black border text-white" style="top: 5px; left: 5px;aspect-ratio: 1/1;font-size: 12px;">#{{item.serial}}</p>{%endif%}
{% endif %}
</div>
<p class="text-secondary m-0" style="font-size:12px;">{% if CategoryIndex != 11 %}{{item.asset.name}}{%else%}{{item.badge.name}}{%endif%}</p>
{% if CategoryIndex != 11 %}
{% if item.asset.is_for_sale: %}
{% if item.asset.price_robux == 0 and item.asset.price_tix == 0: %}
<p class="text-robux m-0 ">FREE</p>
{% else %}
{% if item.asset.price_robux != 0: %}
<p class="text-robux m-0">R$ {{item.asset.price_robux}}</p>
{% endif %}
{% if item.asset.price_tix != 0: %}
<p class="text-tickets m-0">T$ {{item.asset.price_tix}}</p>
{% endif %}
{% endif %}
{% if item.asset.is_limited_unique : %}
<p class="text-remaining m-0">Remaining: <span class="text-white">{{item.asset.serial_count - item.asset.sale_count}}</span></p>
{%endif%}
{% else %}
{% if item.asset.is_limited: %}
{% if item.asset.price_robux == 0 and item.asset.price_tix != 0: %}
<p class="text-tickets m-0"><span class="text-secondary fw-normal">was</span> T$ {{item.asset.price_tix or "-"}}</p>
{% elif item.asset.price_robux != 0 and item.asset.price_tix != 0: %}
<p class="text-robux m-0"><span class="text-secondary fw-normal">was</span> R$ {{item.asset.price_robux or "-"}}</p>
{% elif item.asset.price_robux != 0 and item.asset.price_tix == 0: %}
<p class="text-robux m-0"><span class="text-secondary fw-normal">was</span> R$ {{item.asset.price_robux or "-"}}</p>
{% else %}
<p class="text-robux m-0"><span class="text-secondary fw-normal">was</span> FREE</p>
{% endif %}
<p class="text-robux m-0"><span class="text-secondary fw-normal">now</span> R$ {{item.best_price}}</p>
{%else%}
<p class="text-secondary m-0 ">Offsale</p>
{% endif %}
{% endif %}
{% endif %}
</div></a>
</div>
{% endfor %}
</div>
{% if len(SearchQuery.items) == 0: %}
<p class="w-100 text-center mt-5 mb-5">No items found</p>
{% endif %}
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
<a class="ms-auto m-0 text-decoration-none {% if not SearchQuery.has_prev %}text-secondary{%endif%}" {% if SearchQuery.has_prev %}href="/users/{{profile.id}}/inventory?page={{SearchQuery.prev_num}}&category={{CategoryIndex}}"{%endif%}>Previous</a>
<p class="ms-2 me-2 text-white m-0">Page {{SearchQuery.page}} of {{SearchQuery.pages}}</p>
<a class="me-auto m-0 text-decoration-none {% if not SearchQuery.has_next %}text-secondary{%endif%}" {% if SearchQuery.has_next %}href="/users/{{profile.id}}/inventory?page={{SearchQuery.next_num}}&category={{CategoryIndex}}"{%endif%}>Next</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
+254
View File
@@ -0,0 +1,254 @@
{% extends '__layout__.html' %}
{% block title %}{{profile.username}}{% endblock %}
{% block head %}
<link href="/static/css/profile.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
<style>
.text-secondary {
color: rgb(199, 199, 199) !important;
}
.border {
border-color: rgb(60,60,60) !important;
}
.nav-link {
color: #c7c7c7!important
}
.nav-link.active {
color: #fff!important
}
.place-card {
height: 220px !important;
aspect-ratio: 0.640909/1;
}
.place-card-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 0px;
}
</style>
{% endblock %}
{% block content %}
<div id="main">
<div class="container" style="max-width: 800px;">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div>
{% for message in messages %}
<div class="alert border border-danger p-2 text-center messagealerts">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="row mb-2">
<div class="col-md-7 d-flex">
<div class="image-container">
<img class="rounded overflow-hidden" src="/Thumbs/Head.ashx?x=150&y=150&userId={{profile.id}}" width="130px" height="130px" alt="{{profile.username}}"/>
{% if profile.isonline: %}<div class="status-icon {% if profile.ingame: %}status-icon-green{% endif %}"></div>{% endif %}
</div>
<div>
<div class="d-flex align-items-center">
<h1 class="m-0">{{profile.username}}</h1>
{% if profile.membershipValue == 1: %}
<span class="rbx-icon-bc ms-2"></span>
{%endif%}
{% if profile.membershipValue == 2: %}
<span class="rbx-icon-tbc ms-2"></span>
{%endif%}
{% if profile.membershipValue == 3: %}
<span class="rbx-icon-negative-obc ms-2"></span>
{%endif%}
</div>
{% if profile.id != currentuser.id : %}
{% if profile.doestargetuserfollowviewer: %}
<p class="m-0" style="font-size: 11px;">This user is currently following you</p>
{%endif%}
{%endif%}
<div class="d-flex mt-1">
<div style="width: fit-content;" class="me-3">
<p class="m-0 text-secondary w-100 text-center d-block" style="font-size: 15px;">Friends</p>
<a href="/users/{{profile.id}}/friends" class="m-0 text-decoration-none text-white w-100 text-center d-block" style="font-size: 25px;">{{profile.friendcount}}</a>
</div>
<div style="width: fit-content;" class="me-3">
<p class="m-0 text-secondary w-100 text-center d-block" style="font-size: 15px;">Followers</p>
<a href="/users/{{profile.id}}/followers" class="m-0 text-decoration-none text-white w-100 text-center d-block" style="font-size: 25px;">{{profile.FollowerCount}}</a>
</div>
<div style="width: fit-content;" class="me-3">
<p class="m-0 text-secondary w-100 text-center d-block" style="font-size: 15px;">Following</p>
<a href="/users/{{profile.id}}/following" class="m-0 text-decoration-none text-white w-100 text-center d-block" style="font-size: 25px;">{{profile.FollowingCount}}</a>
</div>
<div style="width: fit-content;">
<p class="m-0 text-secondary w-100 text-center d-block" style="font-size: 15px;">User RAP</p>
<p class="m-0 text-decoration-none text-white w-100 text-center d-block" style="font-size: 25px;">{{profile.UserRAP}}</p>
</div>
</div>
</div>
</div>
<div class="col-md-5">
<div>
<div class="dropdown d-flex justify-content-end">
<button class="btn" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
{% if profile.id != currentuser.id : %}
{% if not profile.isviewerfollowing: %}
<li><form action="/users/{{profile.id}}/followuser" method="post"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/><button class="dropdown-item" type="submit">Follow</button></form></li>
{%else%}
<li><form action="/users/{{profile.id}}/unfollowuser" method="post"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/><button class="dropdown-item" type="submit">Unfollow</button></form></li>
{%endif%}
<li><a class="dropdown-item" href="/trade/{{profile.id}}/create">Trade</a></li>
{% endif %}
<li><a class="dropdown-item" href="/users/{{profile.id}}/inventory">Inventory</a></li>
</ul>
</div>
{% if profile.id != currentuser.id : %}
<div class="d-flex justify-content-end me-3 mt-4">
<a href="/messages/new/{{profile.id}}"><button class="btn border me-2 text-white">Message</button></a>
{% if profile.isfriends: %}
<form action="/users/{{profile.id}}/unfriend" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button class="btn border-danger text-danger">Unfriend</button>
</form>
{% else: %}
{% if profile.friendrequestpending: %}
<button class="btn border" disabled>Pending</button>
{% else: %}
{% if profile.friendrequesttoauthenticateduser: %}
<form action="/users/{{profile.id}}/acceptfriend" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button class="btn border-success">Accept Friend Request</button>
</form>
{% else: %}
<form action="/users/{{profile.id}}/addfriend" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button class="btn border text-white">Add Friend</button>
</form>
{% endif %}
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
<nav class="mt-3 rounded-top mb-2" style="background-color: rgb(40, 40, 40) !important;overflow: hidden;">
<div class="nav nav-underline nav-fill page-tabs" id="nav-tab" role="tablist">
<a href="#about" class="nav-link active" id="nav-about-tab" data-bs-toggle="tab" data-bs-target="#nav-about" type="button" role="tab" aria-controls="nav-about" aria-selected="true">About</a>
<a href="#games" class="nav-link" id="nav-games-tab" data-bs-toggle="tab" data-bs-target="#nav-games" type="button" role="tab" aria-controls="nav-games" aria-selected="false">Games</a>
</div>
</nav>
<div class="tab-content ps-2 pe-2 pt-1 pb-3" id="nav-tabContent">
<div class="tab-pane show active" id="nav-about" role="tabpanel" aria-labelledby="nav-about-tab" tabindex="0">
<h5 class="m-0 mb-1">Description</h5>
{% for line in descriptionlines: %}
{% if line.strip() != '' %}
<p class="text-secondary m-0">{{line}}</p>
{%else%}
<br>
{%endif%}
{% endfor %}
<div class="linebreak"></div>
<div class="row">
<div class="col-md-6 p-2">
<img src="/Thumbs/Avatar.ashx?x=420&y=420&userId={{profile.id}}" class="w-100 border rounded" alt="{{profile.username}} Avatar" />
</div>
<div class="col-md-6 p-2">
<div class="border rounded w-100 h-100 p-2">
<h5 class="text-center w-100 m-0">Currently wearing</h5>
<div class="row p-1">
{% for asset in profile.wearing %}
<div class="col-3 p-1">
<a href="/catalog/{{asset.asset_id}}/">
<img src="/Thumbs/Asset.ashx?assetId={{asset.asset_id}}&x=100&y=100" width="100%" class="rounded p-1" style="background-color: rgb(30,30,30);">
</a>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="d-flex align-items-center">
<h5>Friends</h5>
<div class="ms-auto">
<a href="/users/{{profile.id}}/friends" class="text-decoration-none">View All ( {{profile.friendcount}} )</a>
</div>
</div>
<div class="rounded dark-container p-2 d-flex">
{% for friend in profile.friendsdata %}
<a href="/users/{{friend.id}}/profile">
<div style="height: fit-content; width: 90px;" class="me-3">
<div class="image-container-friend">
<img class="rounded w-100 d-table" src="/Thumbs/Head.ashx?x=100&y=100&userId={{friend.id}}" style="margin: 0 auto; max-width: 100px;aspect-ratio: 1/1;" alt="{{friend.username}}"/>
{% if friend.isonline: %}<div class="status-icon {% if friend.ingame: %}status-icon-green{% endif %}"></div>{% endif %}
</div>
<p class="text-white w-100 text-center d-inline-block m-0 text-truncate" style="font-size: 12px;">{{friend.username}}</p>
</div>
</a>
{% endfor %}
{% if profile.friendcount == 0: %}
<p style="margin: auto auto;" class="d-table">This user has no friends{% if profile.id != currentuser.id : %}, maybe you should send them a friend request!{% endif %}</p>
{% endif %}
</div>
<div class="rounded mt-3 mb-3 p-2 dark-container row d-flex align-items-center" style="min-height: 75px !important;">
<div class="col text-center">
<b class="text-secondary">Join Date</b>
<h4>{{profile.joindate}}</h4>
</div>
<div class="col text-center">
<b class="text-secondary">Place Visits</b>
<h4>{{profile.TotalVisits}}</h4>
</div>
</div>
</div>
<div class="tab-pane" id="nav-games" role="tabpanel" aria-labelledby="nav-games-tab" tabindex="0">
<h5 class="m-0 mb-1">Games</h5>
<div class="w-100 place-card-container">
{% for place in profile.UserGames: %}
<a class="text-decoration-none p-1" href="/games/{{place.id}}/{{place.slug}}">
<div class="overflow-hidden place-card w-100">
<div class="position-relative">
<img width="100%" style="aspect-ratio: 1/1;" src="/Thumbs/PlaceIcon.ashx?assetId={{place.id}}&x=150&y=150" alt="{{place.name}}">
<div class="position-absolute" style="bottom: 0px;left: 0px;">
<div class="fw-bold bg-dark text-white" style="font-size: 12px;padding: 3px;border-top-right-radius: 4px;">{{place.placeyear.value}}</div>
</div>
</div>
<div class="p-1">
<h5 class="mb-0" style="font-size: 17px;text-overflow: ellipsis;">{{place.name}}</h5>
<div class="d-flex align-items-center">
<p style="margin:0;font-size: 14px;" class="text-secondary"><i class="bi bi-people"></i> {{place.playingcount}} <span style="font-size: 13px;">Playing</span></p>
</div>
<div class="w-100 votePercentageBackground">
<div class="votePercentageFill" style="width: {{place.likePercentage}}%;"></div>
<div>
<div class="segment" style="left: 18%;"></div>
<div class="segment" style="left: 38%;"></div>
<div class="segment" style="left: 58%;"></div>
<div class="segment" style="left: 78%;"></div>
</div>
</div>
</div>
</div>
</a>
{% endfor %}
</div>
{% if len(profile.UserGames) == 0:%}
<p class="text-center w-100 p-5">This user has no games</p>
{% endif %}
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", async function() {
var Navtabs = document.querySelectorAll(".page-tabs a");
Navtabs.forEach(function(element) {
element.addEventListener("click", function() {
var TabTarget = element.getAttribute("href");
window.location.hash = TabTarget;
});
});
});
</script>
{% endblock %}
+536
View File
@@ -0,0 +1,536 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify, make_response, flash, abort
from app.util import auth, friends, membership, websiteFeatures, placeinfo
from app.extensions import db, limiter, csrf, user_limiter
from app.models.user import User
from app.models.friend_request import FriendRequest
from app.models.friend_relationship import FriendRelationship
from datetime import datetime
from app.models.asset import Asset
from app.models.place import Place
from app.models.placeserver_players import PlaceServerPlayer
from app.models.follow_relationship import FollowRelationship
from app.models.user_avatar_asset import UserAvatarAsset
from app.models.userassets import UserAsset
from app.models.universe import Universe
from app.models.place_badge import PlaceBadge, UserBadge
from app.enums.AssetType import AssetType
from sqlalchemy import or_
from app.enums.MembershipType import MembershipType
from slugify import slugify
from app.routes.rate import GetAssetLikesAndDislikes, GetUserVoteStatus, GetAssetVotePercentage, GetAssetFavoriteCount, GetUserFavoriteStatus
from app.services import economy
from app.services.user_relationships import followings
Profile = Blueprint("profile", __name__, template_folder="pages")
@Profile.route("/users/<int:userid>/profile", methods=["GET"])
@auth.authenticated_required
def profile_page(userid):
user : User = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
Friends = friends.GetFriends(user.id)
FriendCount = len(Friends)
DescriptionLines = user.description.split("\n")
FriendsData = []
for friend in Friends:
friendObjData = {}
friendObjData = {
"id": friend.id,
"username": friend.username,
"isonline": True if (datetime.utcnow() - friend.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=friend.id).first() is not None else False
}
FriendsData.append(friendObjData)
FriendsData.sort(key=lambda x: x["isonline"], reverse=True)
FriendsData.sort(key=lambda x: x["ingame"], reverse=True)
FriendsData = FriendsData[:8]
AuthenticatedUser = auth.GetCurrentUser()
if AuthenticatedUser.id != user.id:
isFriends = friends.IsFriends(AuthenticatedUser.id, user.id)
FriendRequestPending = FriendRequest.query.filter_by(requester_id=AuthenticatedUser.id, requestee_id=user.id).first() is not None
FriendRequestToAuthenticatedUser = FriendRequest.query.filter_by(requester_id=user.id, requestee_id=AuthenticatedUser.id).first() is not None
isViewerFollowing = FollowRelationship.query.filter_by(followerUserId=AuthenticatedUser.id, followeeUserId=user.id).first() is not None
doesTargetUserFollowViewer = FollowRelationship.query.filter_by(followerUserId=user.id, followeeUserId=AuthenticatedUser.id).first() is not None
else:
isFriends = False
FriendRequestPending = False
FriendRequestToAuthenticatedUser = False
isViewerFollowing = False
doesTargetUserFollowViewer = False
joindate = user.created.strftime("%d/%m/%Y")
TotalVisits = 0
UniverseList : list[Universe] = Universe.query.filter_by( creator_id = user.id, creator_type = 0 ).all()
for UniverseObj in UniverseList:
TotalVisits += UniverseObj.visit_count
FollowerCount = followings.get_follower_count( user )
FollowingCount = followings.get_following_count( user )
WearingAssets = UserAvatarAsset.query.filter_by(user_id=user.id).all()
membershipValue : int = membership.GetUserMembership(user.id).value
UserGames : list[Asset] = Asset.query.filter_by(creator_id = user.id, creator_type = 0, asset_type = AssetType.Place).all()
UserGamesInfo : list[dict] = []
for UserGame in UserGames:
PlaceObj : Place = Place.query.filter_by(placeid=UserGame.id).first()
if PlaceObj is None:
continue
if PlaceObj.is_public == False:
continue
PlaceObjData = {
"id": UserGame.id,
"name": UserGame.name,
"playingcount": placeinfo.GetPlayingCount( PlaceObj ),
"slug": slugify(UserGame.name, lowercase=False) if UserGame.name is not None else "",
"likePercentage": GetAssetVotePercentage(UserGame.id),
"placeyear": PlaceObj.placeyear
}
UserGamesInfo.append(PlaceObjData)
ProfileData = {
"username": user.username,
"id": user.id,
"friendcount": FriendCount,
"isonline": True if (datetime.utcnow() - user.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=user.id).first() is not None else False,
"isfriends": isFriends,
"friendrequestpending": FriendRequestPending,
"friendrequesttoauthenticateduser": FriendRequestToAuthenticatedUser,
"friendsdata": FriendsData,
"joindate": joindate,
"TotalVisits": TotalVisits,
"FollowerCount": FollowerCount,
"FollowingCount": FollowingCount,
"wearing": WearingAssets,
"isviewerfollowing": isViewerFollowing,
"doestargetuserfollowviewer": doesTargetUserFollowViewer,
"membershipValue": membershipValue,
"UserGames": UserGamesInfo,
"UserRAP": economy.CalculateUserRAP( user )
}
return render_template("profiles/profile.html", profile=ProfileData, descriptionlines=DescriptionLines)
@Profile.route("/users/<int:userid>/addfriend", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
@user_limiter.limit("5/minute")
def add_friend(userid):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
if not websiteFeatures.GetWebsiteFeature("SendFriendRequest"):
flash("Adding friends is temporarily disabled.", "error")
return redirect(f"/users/{user.id}/profile")
AuthenticatedUser = auth.GetCurrentUser()
if userid == AuthenticatedUser.id:
flash("You cannot add yourself as a friend.", "error")
return redirect(f"/users/{user.id}/profile")
if FriendRequest.query.filter_by(requester_id=AuthenticatedUser.id, requestee_id=user.id).first() is not None:
flash("You have already sent a friend request to this user.", "error")
return redirect(f"/users/{user.id}/profile")
if friends.IsFriends(AuthenticatedUser.id, user.id):
flash("You are already friends with this user.", "error")
return redirect(f"/users/{user.id}/profile")
friendRequest = FriendRequest(requester_id=AuthenticatedUser.id, requestee_id=user.id)
db.session.add(friendRequest)
db.session.commit()
return redirect(f"/users/{user.id}/profile")
@Profile.route("/users/<int:userid>/unfriend", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
@user_limiter.limit("5/minute")
def unfriend(userid):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
AuthenticatedUser = auth.GetCurrentUser()
if not friends.IsFriends(AuthenticatedUser.id, user.id):
flash("You are not friends with this user.", "error")
return handle_redirect(user)
Friendrelationship : FriendRelationship = friends.GetFriendRelationship(AuthenticatedUser.id, user.id)
db.session.delete(Friendrelationship)
db.session.commit()
return handle_redirect(user)
def handle_redirect(user):
wantRedirect = request.args.get("redirect", type=int, default=1)
if wantRedirect == 1:
return redirect(f"/users/{user.id}/profile")
else:
return redirect(request.referrer)
@Profile.route("/users/<int:userid>/acceptfriend", methods=["POST"])
@limiter.limit("30/minute")
@auth.authenticated_required
@user_limiter.limit("30/minute")
def accept_friend(userid):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
if not websiteFeatures.GetWebsiteFeature("SendFriendRequest"):
flash("Adding friends is temporarily disabled.", "error")
return handle_redirect(user)
AuthenticatedUser = auth.GetCurrentUser()
if userid == AuthenticatedUser.id:
flash("You cannot add yourself as a friend.", "error")
return handle_redirect(user)
friendRequest = FriendRequest.query.filter_by(requester_id=user.id, requestee_id=AuthenticatedUser.id).first()
if friendRequest is None:
flash("Friend request not found.", "error")
return handle_redirect(user)
FriendCount = len(friends.GetFriends(user.id))
if FriendCount >= 200:
flash("This user has reached their friend limit.", "error")
return handle_redirect(user)
AuthenticatedUserFriendCount = len(friends.GetFriends(AuthenticatedUser.id))
if AuthenticatedUserFriendCount >= 200:
flash("You have reached your friend limit.", "error")
return handle_redirect(user)
Friendrelationship = FriendRelationship(user_id=user.id, friend_id=AuthenticatedUser.id)
db.session.add(Friendrelationship)
db.session.delete(friendRequest)
db.session.commit()
return handle_redirect(user)
@Profile.route("/users/<int:userid>/declinefriend", methods=["POST"])
@limiter.limit("30/minute")
@auth.authenticated_required
@user_limiter.limit("30/minute")
def decline_friend(userid):
TargetUser = User.query.filter_by(id=userid).first()
if TargetUser is None:
abort(404)
if not websiteFeatures.GetWebsiteFeature("SendFriendRequest"):
flash("Declining friend requests is temporarily disabled.", "error")
return handle_redirect(TargetUser)
AuthenticatedUser = auth.GetCurrentUser()
if userid == AuthenticatedUser.id:
abort(404)
friendRequest = FriendRequest.query.filter_by(requester_id=TargetUser.id, requestee_id=AuthenticatedUser.id).first()
if friendRequest is None:
return handle_redirect(TargetUser)
db.session.delete(friendRequest)
db.session.commit()
return handle_redirect(TargetUser)
@Profile.route("/users/<int:userid>/unfollowuser", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
def unfollow_user(userid):
UserObj : User = User.query.filter_by(id=userid).first()
if UserObj is None:
abort(404)
AuthenticatedUser : User = auth.GetCurrentUser()
try:
followings.unfollow_user(
current_follower = AuthenticatedUser, followed_user = UserObj
)
except followings.FollowingExceptions.UserNotFollowing:
flash("You are not following this user.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.UserRateLimited:
flash("You are being rate limited from unfollowing users, please slow down.", "error")
return redirect(f"/users/{UserObj.id}/profile")
return redirect(f"/users/{UserObj.id}/profile")
@Profile.route("/users/<int:userid>/followuser", methods=["POST"])
@limiter.limit("5/minute")
@auth.authenticated_required
def follow_user(userid):
UserObj : User = User.query.filter_by( id = userid ).first()
if UserObj is None:
abort(404)
if UserObj.accountstatus == 4 or UserObj.accountstatus == 3:
abort(404)
AuthenticatedUser : User = auth.GetCurrentUser()
try:
followings.follow_user(
follower_user = AuthenticatedUser, followed_user = UserObj
)
except followings.FollowingExceptions.AlreadyFollowing:
flash("You are already following this user.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.UserRateLimited:
flash("You are being rate limited from following users, please slow down.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.CannotFollowSelf:
flash("You cannot follow yourself.", "error")
return redirect(f"/users/{UserObj.id}/profile")
except followings.FollowingExceptions.FollowingIsDisabled:
flash("Following users is disabled.", "error")
return redirect(f"/users/{UserObj.id}/profile")
return redirect(f"/users/{UserObj.id}/profile")
@Profile.route("/users/<int:userid>/friends", methods=["GET"])
@auth.authenticated_required
def FriendsPage( userid : int ):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
from app.models.friend_relationship import FriendRelationship
FriendRelationships : list[FriendRelationship] = FriendRelationship.query.filter(
or_(
FriendRelationship.user_id == user.id,
FriendRelationship.friend_id == user.id
)
).join(
User,
or_(
FriendRelationship.user_id == User.id,
FriendRelationship.friend_id == User.id
)
).filter(
User.id != user.id
).order_by(
User.username
).paginate(
page=PageNumber,
per_page=18,
error_out=False
)
FriendUsers : list[User] = []
for FriendRelationshipObj in FriendRelationships:
if FriendRelationshipObj.user_id == user.id:
TargetUserId : int = FriendRelationshipObj.friend_id
else:
TargetUserId : int = FriendRelationshipObj.user_id
FriendUser : User = User.query.filter_by(id=TargetUserId).first()
if FriendUser is not None:
FriendUsers.append({
"id": FriendUser.id,
"username": FriendUser.username,
"isonline": True if (datetime.utcnow() - FriendUser.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=FriendUser.id).first() is not None else False
})
FriendCount = FriendRelationship.query.filter((FriendRelationship.user_id == user.id) | (FriendRelationship.friend_id == user.id)).count()
if FriendRelationships.pages < PageNumber:
redirect(f"/users/{user.id}/friends")
return render_template("profiles/friends.html",
profile=user,
FriendUsers=FriendUsers,
PageNumber=PageNumber,
isThereNextPage=FriendRelationships.has_next,
isTherePreviousPage=FriendRelationships.has_prev,
FriendCount=FriendCount,
TotalPages=FriendRelationships.pages)
@Profile.route("/users/<int:userid>/following", methods=["GET"])
@auth.authenticated_required
def FollowingPage( userid : int ):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
from app.models.follow_relationship import FollowRelationship
FollowRelationships : list[FollowRelationship] = FollowRelationship.query.filter_by(followerUserId=user.id).join(
User,
FollowRelationship.followeeUserId == User.id
).order_by(
User.username
).paginate(
page=PageNumber,
per_page=18,
error_out=False
)
FollowUsers : list[User] = []
for FollowRelationship in FollowRelationships:
FollowUser : User = User.query.filter_by(id=FollowRelationship.followeeUserId).first()
if FollowUser is not None:
FollowUsers.append({
"id": FollowUser.id,
"username": FollowUser.username,
"isonline": True if (datetime.utcnow() - FollowUser.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=FollowUser.id).first() is not None else False
})
FollowCount = FollowRelationship.query.filter_by(followerUserId=user.id).count()
if FollowRelationships.pages < PageNumber:
redirect(f"/users/{user.id}/following")
return render_template("profiles/following.html",
profile=user,
FollowUsers=FollowUsers,
PageNumber=PageNumber,
isThereNextPage=FollowRelationships.has_next,
isTherePreviousPage=FollowRelationships.has_prev,
FollowCount=FollowCount,
TotalPages=FollowRelationships.pages)
@Profile.route("/users/<int:userid>/followers", methods=["GET"])
@auth.authenticated_required
def FollowersPage( userid : int ):
user = User.query.filter_by(id=userid).first()
if user is None:
abort(404)
if user.accountstatus == 4 or user.accountstatus == 3:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
from app.models.follow_relationship import FollowRelationship
FollowRelationships : list[FollowRelationship] = FollowRelationship.query.filter_by(followeeUserId=user.id).join(
User,
FollowRelationship.followerUserId == User.id
).order_by(
User.username
).paginate(
page=PageNumber,
per_page=18,
error_out=False
)
FollowUsers : list[User] = []
for FollowRelationship in FollowRelationships:
FollowUser : User = User.query.filter_by(id=FollowRelationship.followerUserId).first()
if FollowUser is not None:
FollowUsers.append({
"id": FollowUser.id,
"username": FollowUser.username,
"isonline": True if (datetime.utcnow() - FollowUser.lastonline).total_seconds() < 60 else False,
"ingame": True if PlaceServerPlayer.query.filter_by(userid=FollowUser.id).first() is not None else False
})
FollowCount = FollowRelationship.query.filter_by(followeeUserId=user.id).count()
if FollowRelationships.pages < PageNumber:
redirect(f"/users/{user.id}/followers")
return render_template("profiles/followers.html",
profile=user,
FollowUsers=FollowUsers,
PageNumber=PageNumber,
isThereNextPage=FollowRelationships.has_next,
isTherePreviousPage=FollowRelationships.has_prev,
FollowCount=FollowCount,
TotalPages=FollowRelationships.pages)
@Profile.route("/users/<int:userid>/requests", methods=["GET"])
@auth.authenticated_required
def FriendRequestsPage( userid : int ):
AuthenticatedUser = auth.GetCurrentUser()
if AuthenticatedUser.id != userid:
abort(404)
PageNumber = request.args.get("page", 1, type=int)
FriendRequests : list[FriendRequest] = FriendRequest.query.filter_by(requestee_id=userid).order_by(FriendRequest.created_at.desc()).paginate(page=PageNumber, per_page=18, error_out=False)
return render_template("profiles/requests.html",
profile=AuthenticatedUser,
FriendRequests=FriendRequests)
@Profile.route("/users/<int:userid>/requests/clear", methods=["POST"])
@auth.authenticated_required
def ClearFriendRequests(userid : int):
AuthenticatedUser = auth.GetCurrentUser()
if AuthenticatedUser.id != userid:
abort(404)
FriendRequests : list[FriendRequest] = FriendRequest.query.filter_by(requestee_id=userid).all()
for FriendRequestObj in FriendRequests:
db.session.delete(FriendRequestObj)
db.session.commit()
return redirect(f"/users/{userid}/requests")
@Profile.route("/users/<int:userid>/inventory", methods=["GET"])
@auth.authenticated_required
def UserInventoryPage( userid : int ):
UserObj : User = User.query.filter_by( id = userid ).first()
if UserObj is None:
return abort(404)
InventoryTypesDict = {
0: lambda queryObj: queryObj.filter(Asset.asset_type.in_((
AssetType.HairAccessory,
AssetType.FaceAccessory,
AssetType.NeckAccessory,
AssetType.ShoulderAccessory,
AssetType.FrontAccessory,
AssetType.BackAccessory,
AssetType.WaistAccessory,
))),
1: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Audio ),
2: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Package ),
3: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Face ),
4: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Head ),
5: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Hat ),
6: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.GamePass ),
7: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.TShirt ),
8: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Shirt ),
9: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Pants ),
10: lambda queryObj: queryObj.filter( Asset.asset_type == AssetType.Gear )
}
InventoryTypeToFriendlyName = {
0: "Accessories",
1: "Audio",
2: "Packages",
3: "Faces",
4: "Heads",
5: "Hats",
6: "Passes",
7: "T-Shirts",
8: "Shirts",
9: "Pants",
10: "Gears",
11: "Badges"
}
CategoryType = request.args.get( key = "category", default = 0, type = int )
if CategoryType not in InventoryTypeToFriendlyName:
return redirect(f"/users/{userid}/inventory")
PageNumber = request.args.get( key = "page", default = 1, type = int )
if PageNumber < 1:
PageNumber = 1
if CategoryType not in [11]:
SearchQuery = UserAsset.query.filter_by( userid = userid ).outerjoin( Asset, Asset.id == UserAsset.assetid )
SearchQuery = InventoryTypesDict[CategoryType](SearchQuery).order_by( UserAsset.updated.desc() ).paginate( page = PageNumber, per_page = 24, error_out = False )
for UserAssetObj in SearchQuery.items:
if UserAssetObj.asset.is_limited and not UserAssetObj.asset.is_for_sale:
BestPriceResult : UserAsset = UserAsset.query.filter_by(assetid=UserAssetObj.assetid, is_for_sale=True).order_by(UserAsset.price.asc()).first()
if BestPriceResult is not None:
UserAssetObj.best_price = str(BestPriceResult.price)
else:
UserAssetObj.best_price = "--"
elif CategoryType == 11:
SearchQuery = UserBadge.query.filter_by( user_id = userid ).order_by( UserBadge.awarded_at.desc() ).paginate( page = PageNumber, per_page = 24, error_out = False )
return render_template(
"profiles/inventory.html",
profile = UserObj,
SearchQuery = SearchQuery,
CategoryName = InventoryTypeToFriendlyName[CategoryType],
CategoryIndex = CategoryType
)
+82
View File
@@ -0,0 +1,82 @@
{% extends '__layout__.html' %}
{% block title %}Friend Requests{% endblock %}
{% block head %}
<link href="/static/css/profile.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
<style>
.text-secondary {
color: rgb(199, 199, 199) !important;
}
.border {
border-color: rgb(60,60,60) !important;
}
.nav-link {
color: rgb(199, 199, 199) !important;
}
.nav-link.active {
color: rgb(255, 255, 255) !important;
}
</style>
{% endblock %}
{% block content %}
<div id="main">
<div class="container" style="max-width: 800px;">
<div class="d-flex align-items-center">
<a href="/users/{{profile.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{profile.id}}" width="80px" height="80px" class="rounded-2">
</a>
<h2 class="ms-3">Friend Requests</h2>
</div>
<nav class="mt-2 rounded-top" style="background-color: rgb(40, 40, 40) !important;overflow: hidden;">
<div class="nav nav-underline nav-fill" id="nav-tab" role="tablist">
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/friends">Friends</a>
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/following">Following</a>
<a class="nav-link" type="button" role="tab" aria-selected="false" href="/users/{{profile.id}}/followers">Followers</a>
{% if profile.id == currentuser.id %}
<a class="nav-link active" type="button" role="tab" aria-selected="true">Requests{% if currentuser.friend_requests > 0: %}<span class="badge text-bg-danger ms-1">{{currentuser.friend_requests}}</span>{%endif%}</a>
{%endif%}
</div>
</nav>
<div>
<div class="d-flex align-items-center">
<h4 class="m-0 mt-2">Requests ( {{FriendRequests.total}} )</h4>
<form class="ms-auto mt-2" method="post" action="/users/{{profile.id}}/requests/clear">
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
<button class="btn btn-danger btn-sm" type="submit">Deny All</button>
</form>
</div>
<div class="row mt-2">
{% for request in FriendRequests: %}
<div class="col-6 col-sm-4 mb-2">
<div class="bg-dark border p-2 rounded d-flex">
<a href="/users/{{request.requester.id}}/profile">
<img src="/Thumbs/Head.ashx?x=100&y=100&userId={{request.requester.id}}" width="80px" height="80px" class="rounded-2" alt="{{request.requester.username}}">
</a>
<div class="h-100">
<a class="text-white text-decoration-none ms-2" href="/users/{{request.requester.id}}/profile">{{request.requester.username}}</a>
<div class="ms-2 mt-4 input-group flex-nowrap" >
<form method="post" action="/users/{{request.requester.id}}/acceptfriend?redirect=0">
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
<button class="btn btn-success btn-sm rounded-end-0" type="submit"><span class="bi bi-check-lg"></span></button>
</form>
<form method="post" action="/users/{{request.requester.id}}/declinefriend?redirect=0">
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
<button class="btn btn-danger btn-sm rounded-start-0" type="submit"><span class="bi bi-x-lg"></span></button>
</form>
</div>
</div>
</div>
</div>
{%endfor%}
</div>
{% if FriendRequests.total <= 0: %}
<p class="w-100 text-secondary text-center text-white mt-4 mb-4">No results found</p>
{% endif %}
</div>
<div class="d-flex mt-2 align-items-center">
<a class="ms-auto text-decoration-none {%if not FriendRequests.has_prev:%}text-secondary{%endif%}" {%if FriendRequests.has_prev: %}href="/users/{{profile.id}}/requests?page={{FriendRequests.page - 1}}{%endif%}">Previous</a>
<p class="m-0 ms-2 me-2 text-white">Page {{FriendRequests.page}} of {{FriendRequests.pages}}</p>
<a class="me-auto text-decoration-none {%if not FriendRequests.has_next: %}text-secondary{%endif%}" {%if FriendRequests.has_next: %}href="/users/{{profile.id}}/requests?page={{FriendRequests.page + 1}}{%endif%}">Next</a>
</div>
</div>
</div>
{% endblock %}