Skip to content

Commit b3de1a0

Browse files
committed
Add badges system
1 parent 9b7f116 commit b3de1a0

File tree

56 files changed

+4665
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+4665
-3
lines changed

.cspell/people.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ roardom
44
sindre
55
sindresorhus
66
sorhus
7-
vinnie
7+
vinnie
8+
obi-wana

app/Http/Controllers/AuthenticatedImageController.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
use App\Models\Article;
2020
use App\Models\Category;
21+
use App\Models\Collectible;
2122
use App\Models\Playlist;
2223
use App\Models\Torrent;
2324
use App\Models\User;
@@ -93,4 +94,13 @@ public function userIcon(User $user): \Symfony\Component\HttpFoundation\BinaryFi
9394

9495
return response()->file($path, self::HEADERS);
9596
}
97+
98+
public function collectibleIcon(Collectible $collectible): \Symfony\Component\HttpFoundation\BinaryFileResponse
99+
{
100+
$path = Storage::disk('collectible-icons')->path($collectible->icon);
101+
102+
abort_unless(file_exists($path), 404);
103+
104+
return response()->file($path, self::HEADERS);
105+
}
96106
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* NOTICE OF LICENSE.
7+
*
8+
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
9+
* The details is bundled with this project in the file LICENSE.txt.
10+
*
11+
* @project UNIT3D Community Edition
12+
*
13+
* @author Obi-Wana
14+
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
15+
*/
16+
17+
namespace App\Http\Controllers\Collectible;
18+
19+
use App\Http\Controllers\Controller;
20+
use App\Models\CollectibleCategory;
21+
use App\Models\Collectible;
22+
use App\Models\User;
23+
use Illuminate\Support\Facades\DB;
24+
25+
class CollectibleController extends Controller
26+
{
27+
/**
28+
* Display All Collectibles.
29+
*/
30+
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
31+
{
32+
return view('collectible.index', [
33+
'collectibles' => CollectibleCategory::with('collectibles')->has('collectibles')->orderBy('position')->get(),
34+
]);
35+
}
36+
37+
/**
38+
* Display A Collectible.
39+
*/
40+
public function show(Collectible $collectible): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
41+
{
42+
$user = User::withAvg('history as avg_seedtime', 'seedtime')
43+
->findOrFail(auth()->user()->id);
44+
$userAvgSeedtime = DB::table('history')->where('user_id', '=', $user->id)->avg('seedtime');
45+
$userSeedSize = $user->seedingTorrents()->sum('size');
46+
47+
$requirements = $collectible->requirements;
48+
49+
return view('collectible.show', [
50+
'user' => $user,
51+
'userAvgSeedtime' => $userAvgSeedtime,
52+
'userSeedSize' => $userSeedSize,
53+
'collectible' => $collectible,
54+
'userOwns' => $collectible->items()->where('user_id', '=', $user->id)->exists(),
55+
'transactions' => $collectible->transactions()->latest()->take(50)->get(),
56+
'offers' => $collectible->offers()->whereNull('filled_at')->get(),
57+
'userIsSelling' => $collectible->offers()->where('user_id', $user->id)->whereNull('filled_at')->exists(),
58+
'userTransaction' => $collectible->transactions()->where('buyer_id', $user->id)->latest()->first(),
59+
'availableCount' => $collectible->items()->whereNull('user_id')->count(),
60+
'requirements' => $requirements,
61+
'userMeetsAllRequirements' => (
62+
($requirements->min_uploaded === null || $user->uploaded >= $requirements->min_uploaded)
63+
&& ($requirements->min_seedsize === null || $userSeedSize >= $requirements->min_seedsize)
64+
&& ($requirements->min_avg_seedtime === null || $userAvgSeedtime >= $requirements->min_avg_seedtime)
65+
&& ($requirements->min_ratio === null || $user->ratio >= $requirements->min_ratio)
66+
&& ($requirements->min_age === null || $user->created_at->diffInSeconds(now()) >= $requirements->min_age)
67+
),
68+
]);
69+
}
70+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* NOTICE OF LICENSE.
7+
*
8+
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
9+
* The details is bundled with this project in the file LICENSE.txt.
10+
*
11+
* @project UNIT3D Community Edition
12+
*
13+
* @author Obi-Wana
14+
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
15+
*/
16+
17+
namespace App\Http\Controllers\Collectible;
18+
19+
use App\Http\Controllers\Controller;
20+
use App\Http\Requests\StoreCollectibleOfferRequest;
21+
use App\Models\Collectible;
22+
use App\Models\CollectibleOffer;
23+
use App\Models\CollectibleTransaction;
24+
use App\Models\User;
25+
use Illuminate\Http\Request;
26+
use Illuminate\Support\Facades\DB;
27+
28+
class CollectibleOfferController extends Controller
29+
{
30+
/**
31+
* Create A Collectible Offer.
32+
*
33+
* User creates a new offer.
34+
*/
35+
public function create(Collectible $collectible): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
36+
{
37+
return view('collectible.offer.create', [
38+
'collectibleItem' => $collectible->items()->where('user_id', '=', auth()->user()->id)->first(),
39+
]);
40+
}
41+
42+
/**
43+
* Store A New Collectible Offer.
44+
*
45+
* Store User creates a new offer.
46+
*/
47+
public function store(Collectible $collectible, StoreCollectibleOfferRequest $request): \Illuminate\Http\RedirectResponse
48+
{
49+
$request->validated('offer');
50+
51+
$user = auth()->user();
52+
53+
$userOwns = $collectible->items()->where('user_id', '=', $user->id)->exists();
54+
$userIsSelling = $collectible->offers()->where('user_id', $user->id)->whereNull('filled_at')->exists();
55+
56+
if (! $collectible->resell) {
57+
return to_route('collectibles.show', ['collectible' => $collectible])
58+
->withErrors("Reselling is disabled for this collectible.");
59+
}
60+
61+
if (! $userOwns) {
62+
return to_route('collectibles.show', ['collectible' => $collectible])
63+
->withErrors("You can not sell this item, you do not own it.");
64+
}
65+
66+
if ($collectible->in_stock) {
67+
return to_route('collectibles.show', ['collectible' => $collectible])
68+
->withErrors("You can not sell this item, its still in stock at the regular market.");
69+
}
70+
71+
if ($userIsSelling) {
72+
return to_route('collectibles.show', ['collectible' => $collectible])
73+
->withErrors("You can not sell this item, you are already selling it.");
74+
}
75+
76+
if ($request->offer['price'] > $collectible->price * 1.4) {
77+
return to_route('collectibles.show', ['collectible' => $collectible])
78+
->withErrors("The highest selling price for this item is 1.2 times its original price.");
79+
}
80+
81+
$offer = CollectibleOffer::create([
82+
'collectible_id' => $collectible->id,
83+
'user_id' => $user->id,
84+
'price' => $request->offer['price'],
85+
]);
86+
87+
return to_route('collectibles.show', ['collectible' => $collectible])
88+
->with('success', "Offer created.");
89+
}
90+
91+
/**
92+
* Store An Collectible Offer Accept.
93+
*
94+
* User accepts an existing offer.
95+
*/
96+
public function update(CollectibleOffer $collectibleOffer): \Illuminate\Http\RedirectResponse
97+
{
98+
$user = auth()->user();
99+
$userAvgSeedtime = DB::table('history')->where('user_id', '=', $user->id)->avg('seedtime');
100+
$userSeedSize = $user->seedingTorrents()->sum('size');
101+
102+
$collectible = $collectibleOffer->collectible;
103+
$userOwns = $collectible->items()->where('user_id', '=', $user->id)->exists();
104+
105+
if ($user->id === $collectibleOffer->user_id) {
106+
return to_route('collectibles.show', ['collectible' => $collectible])
107+
->withErrors("You can not accept your own offer.");
108+
}
109+
110+
if ($userOwns) {
111+
return to_route('collectibles.show', ['collectible' => $collectible])
112+
->withErrors("You already own this item.");
113+
}
114+
115+
if ($collectibleOffer->price > $user->seedbonus) {
116+
return to_route('collectibles.show', ['collectible' => $collectible])
117+
->withErrors("Not enough BON.");
118+
}
119+
120+
$requirements = $collectible->requirements;
121+
$userMeetsAllRequirements = (
122+
($requirements->min_uploaded === null || $user->uploaded >= $requirements->min_uploaded)
123+
&& ($requirements->min_seedsize === null || $userSeedSize >= $requirements->min_seedsize)
124+
&& ($requirements->min_avg_seedtime === null || $userAvgSeedtime >= $requirements->min_avg_seedtime)
125+
&& ($requirements->min_ratio === null || $user->ratio >= $requirements->min_ratio)
126+
&& ($requirements->min_age === null || $user->created_at->diffInSeconds(now()) >= $requirements->min_age)
127+
);
128+
129+
if (! $userMeetsAllRequirements) {
130+
return to_route('collectibles.show', ['collectible' => $collectible])
131+
->withErrors("You do not meet all requirements to buy this item!");
132+
}
133+
134+
// Get the offered item
135+
$collectibleItem = $collectible->items()->where('user_id', '=', $collectibleOffer->seller->id)->first();
136+
137+
DB::transaction(static function () use ($collectible, $collectibleItem, $collectibleOffer, $user): void {
138+
$transaction = CollectibleTransaction::create([
139+
'collectible_id' => $collectible->id,
140+
'seller_id' => $collectibleOffer->seller->id,
141+
'buyer_id' => $user->id,
142+
'price' => $collectibleOffer->price,
143+
]);
144+
145+
$collectibleItem->update([
146+
'user_id' => $user->id,
147+
]);
148+
149+
$collectibleOffer->update([
150+
'filled_at' => now(),
151+
]);
152+
153+
User::whereKey($transaction->seller_id)->increment('seedbonus', (float) $transaction->price);
154+
User::whereKey($transaction->buyer_id)->decrement('seedbonus', (float) $transaction->price);
155+
});
156+
157+
return to_route('collectibles.show', ['collectible' => $collectible])
158+
->with('success', "Offer accepted.");
159+
}
160+
161+
/**
162+
* Destroy An Collectible Offer.
163+
*
164+
* User deletes his existing offer.
165+
*/
166+
public function destroy(Request $request, CollectibleOffer $collectibleOffer): \Illuminate\Http\RedirectResponse
167+
{
168+
abort_unless($request->user()->is($collectibleOffer->seller) || $request->user()->group->is_modo, 403);
169+
170+
$collectibleOffer->delete();
171+
172+
return to_route('collectibles.show', ['collectible' => $collectibleOffer->collectible])
173+
->with('success', 'Offer deleted.');
174+
}
175+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* NOTICE OF LICENSE.
7+
*
8+
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
9+
* The details is bundled with this project in the file LICENSE.txt.
10+
*
11+
* @project UNIT3D Community Edition
12+
*
13+
* @author Obi-Wana
14+
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
15+
*/
16+
17+
namespace App\Http\Controllers\Collectible;
18+
19+
use App\Http\Controllers\Controller;
20+
use App\Models\Collectible;
21+
use App\Models\CollectibleTransaction;
22+
use App\Models\User;
23+
use Illuminate\Support\Facades\DB;
24+
25+
class CollectibleTransactionController extends Controller
26+
{
27+
/**
28+
* Buy A Collectible From Store.
29+
*
30+
* User buys in stock collectible.
31+
*/
32+
public function create(Collectible $collectible): \Illuminate\Http\RedirectResponse
33+
{
34+
$user = auth()->user();
35+
$userAvgSeedtime = DB::table('history')->where('user_id', '=', $user->id)->avg('seedtime');
36+
$userSeedSize = $user->seedingTorrents()->sum('size');
37+
38+
$userOwns = $collectible->items()->where('user_id', '=', $user->id)->exists();
39+
40+
if ($userOwns) {
41+
return to_route('collectibles.show', ['collectible' => $collectible])
42+
->withErrors("You already own this item.");
43+
}
44+
45+
if ($collectible->price > $user->seedbonus) {
46+
return to_route('collectibles.show', ['collectible' => $collectible])
47+
->withErrors('Not enough BON.');
48+
}
49+
50+
$requirements = $collectible->requirements;
51+
$userMeetsAllRequirements = (
52+
($requirements->min_uploaded === null || $user->uploaded >= $requirements->min_uploaded)
53+
&& ($requirements->min_seedsize === null || $userSeedSize >= $requirements->min_seedsize)
54+
&& ($requirements->min_avg_seedtime === null || $userAvgSeedtime >= $requirements->min_avg_seedtime)
55+
&& ($requirements->min_ratio === null || $user->ratio >= $requirements->min_ratio)
56+
&& ($requirements->min_age === null || $user->created_at->diffInSeconds(now()) >= $requirements->min_age)
57+
);
58+
59+
if (! $userMeetsAllRequirements) {
60+
return to_route('collectibles.show', ['collectible' => $collectible])
61+
->withErrors("You do not meet all requirements to buy this item!");
62+
}
63+
64+
// Get the first available collectible item that is not owned by a user
65+
$collectibleItem = $collectible->items()->whereNull('user_id')->first();
66+
67+
$transaction = CollectibleTransaction::create([
68+
'collectible_id' => $collectible->id,
69+
'seller_id' => User::SYSTEM_USER_ID,
70+
'buyer_id' => $user->id,
71+
'price' => $collectible->price,
72+
]);
73+
74+
$collectibleItem->update([
75+
'user_id' => $user->id,
76+
]);
77+
78+
$user->decrement('seedbonus', $collectibleItem->collectible->price);
79+
80+
return to_route('collectibles.show', ['collectible' => $collectible])
81+
->with('success', "Collectible bought.");
82+
}
83+
}

0 commit comments

Comments
 (0)