Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: featured collections carousel #494

Merged
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
de8e992
feat: move old collections page to MyCollections folder
patricio0312rev Nov 20, 2023
d29ae71
feat: MyCollectionsController
patricio0312rev Nov 20, 2023
62702fe
feat: update titles for metatags
patricio0312rev Nov 20, 2023
a66efb4
feat: update collection routes
patricio0312rev Nov 20, 2023
7052ccc
feat: blank page for new collections page
patricio0312rev Nov 20, 2023
da45235
chore: update index method for collections page
patricio0312rev Nov 20, 2023
4ee7b57
fix: remove unused imports
patricio0312rev Nov 20, 2023
ac199c8
feat: class names for gradient overlay
patricio0312rev Nov 21, 2023
d4cb7db
feat: CollectionFeaturedData data type
patricio0312rev Nov 21, 2023
5a8e935
chore: update generated.d.ts
patricio0312rev Nov 21, 2023
541cb53
chore: update language file
patricio0312rev Nov 21, 2023
7821005
feat: custom class names for wrapper in GridHeader
patricio0312rev Nov 21, 2023
77e925c
test: GridHeader wrapper class names
patricio0312rev Nov 21, 2023
ea0a58f
feat: custom class names for CollectionNft
patricio0312rev Nov 21, 2023
e7ffacc
feat: return featured collections with cached nfts
patricio0312rev Nov 21, 2023
f6eb1ba
chore: update language file
patricio0312rev Nov 21, 2023
35217d1
feat: set the max number of decimals as a prop
patricio0312rev Nov 21, 2023
54ea797
test: max number of decimals in FormatCrypto
patricio0312rev Nov 21, 2023
abbe9a6
feat: initial export of FeaturedCollectionsSlider
patricio0312rev Nov 21, 2023
3fae3d6
feat: initial implementation for FeaturedCollectionsSlider
patricio0312rev Nov 21, 2023
04e0891
feat: initial mobile design for FeaturedCollectionsSlider card
patricio0312rev Nov 21, 2023
fc2a39c
fix: add test id for grid header wrapper
patricio0312rev Nov 21, 2023
9d928b3
feat: add fade effect for swiper
patricio0312rev Nov 23, 2023
bde0407
feat: featured collections gradient for desktop view
patricio0312rev Nov 23, 2023
858d5c9
feat: FeaturedCollectionsItem
patricio0312rev Nov 23, 2023
eb74321
chore: update exports for FeaturedCollections
patricio0312rev Nov 23, 2023
845f8f6
fix: remove fade effect
patricio0312rev Nov 23, 2023
d17c41b
feat: FeaturedCollectionsCarousel
patricio0312rev Nov 23, 2023
93ea35f
feat: implement carousel
patricio0312rev Nov 23, 2023
5e38d9a
feat: add featured collections for collections seeder
patricio0312rev Nov 23, 2023
04184bf
fix: remove fade effect from carousel
patricio0312rev Nov 23, 2023
e545b4e
fix: remove comments
patricio0312rev Nov 23, 2023
739ec78
feat: FeaturedCollectionInfo
patricio0312rev Nov 23, 2023
7a3acab
fix: set default styles for nft cards
patricio0312rev Nov 23, 2023
329788a
feat: default descriptions for collections seeder
patricio0312rev Nov 23, 2023
82a964a
Merge branch 'feat/collections-overview' into feat/featured-collectio…
patricio0312rev Nov 23, 2023
fff28d1
chore: update language file
patricio0312rev Nov 23, 2023
d79d358
style: resolve style guide violations
patricio0312rev Nov 23, 2023
0276391
style: resolve style guide violations
patricio0312rev Nov 23, 2023
846b5f2
fix: type for DataCollection in CollectionFeaturedData
patricio0312rev Nov 23, 2023
dc40526
feat: setNfts method for collections model
patricio0312rev Nov 23, 2023
273b4b2
fix: set types for setNfts
patricio0312rev Nov 23, 2023
468aa62
fix: remove setNfts method
patricio0312rev Nov 23, 2023
1e2e92c
fix: set cachedNfts as Collection property
patricio0312rev Nov 23, 2023
9b55da7
style: resolve style guide violations
patricio0312rev Nov 23, 2023
8534aea
fix: remove null from cachedNfts type
patricio0312rev Nov 23, 2023
00b0048
Merge branch 'feat/featured-collections-slider' of https://github.com…
patricio0312rev Nov 23, 2023
8320784
test: CollectionController index
patricio0312rev Nov 23, 2023
c6e2753
style: resolve style guide violations
patricio0312rev Nov 23, 2023
93f44bd
fix: restore tests
patricio0312rev Nov 23, 2023
75714ec
fix: restore phpunit
patricio0312rev Nov 23, 2023
7e7a22a
Merge branch 'feat/featured-collections-slider' of https://github.com…
patricio0312rev Nov 23, 2023
57e8099
Update resources/js/Pages/Collections/Components/FeaturedCollections/…
patricio0312rev Nov 24, 2023
fcc63e8
fix: add margin between hero and table
patricio0312rev Nov 24, 2023
fccf8eb
fix: update number of seconds for cache
patricio0312rev Nov 24, 2023
d986fda
feat: FeaturedCollectionStats
patricio0312rev Nov 24, 2023
4797ced
feat: FeaturedCollectionNfts
patricio0312rev Nov 24, 2023
8d1d9d7
Merge branch 'feat/collections-overview' into feat/featured-collectio…
patricio0312rev Nov 24, 2023
60b73b1
style: resolve style guide violations
patricio0312rev Nov 24, 2023
7bde290
fix: featured collections page
patricio0312rev Nov 24, 2023
ae26fb0
feat: CollectionImageWithIcon
patricio0312rev Nov 24, 2023
58b89d6
fix: set fixed width for cards and carousel item in mobile views
patricio0312rev Nov 24, 2023
eb82c9e
fix: add border for rounded carousel item
patricio0312rev Nov 24, 2023
4285d20
fix: width for collection with just 1 nft
patricio0312rev Nov 24, 2023
ebaa573
fix: width for collections with just 1 nft
patricio0312rev Nov 24, 2023
5427fef
Merge branch 'feat/collections-overview' into feat/featured-collectio…
goga-m Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions app/Data/Collections/CollectionFeaturedData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace App\Data\Collections;

use App\Data\Gallery\GalleryNftData;
use App\Enums\CurrencyCode;
use App\Models\Collection;
use App\Transformers\IpfsGatewayUrlTransformer;
use Illuminate\Support\Str;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Attributes\WithTransformer;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
use Spatie\TypeScriptTransformer\Attributes\LiteralTypeScriptType;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
class CollectionFeaturedData extends Data
{
/**
* @param DataCollection<int, GalleryNftData> $nfts
*/
public function __construct(
public int $id,
public string $name,
public string $slug,
public string $address,
#[LiteralTypeScriptType('App.Enums.Chain')]
public int $chainId,
public ?string $floorPrice,
public ?float $floorPriceFiat,
public ?string $floorPriceCurrency,
public ?int $floorPriceDecimals,
#[WithTransformer(IpfsGatewayUrlTransformer::class)]
public ?string $image,
public ?string $banner,
public ?string $openSeaSlug,
public string $website,
public int $nftsCount,
#[DataCollectionOf(GalleryNftData::class)]
public DataCollection $nfts,
public bool $isFeatured,
public ?string $description,
public ?string $volume,
) {
}

public static function fromModel(Collection $collection, CurrencyCode $currency): self
{
return new self(
id: $collection->id,
name: $collection->name,
slug: $collection->slug,
address: $collection->address,
chainId: $collection->network->chain_id,
floorPrice: $collection->floor_price,
floorPriceFiat: (float) $collection->fiatValue($currency),
floorPriceCurrency: $collection->floorPriceToken ? Str::lower($collection->floorPriceToken->symbol) : null,
floorPriceDecimals: $collection->floorPriceToken?->decimals,
image: $collection->extra_attributes->get('image'),
banner: $collection->extra_attributes->get('banner'),
openSeaSlug: $collection->extra_attributes->get('opensea_slug'),
website: $collection->website(),
nftsCount: $collection->nfts_count,
nfts: GalleryNftData::collection($collection->cachedNfts),
isFeatured: $collection->is_featured,
description: $collection->description,
volume: $collection->volume,
);
}
}
15 changes: 15 additions & 0 deletions app/Http/Controllers/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Data\Articles\ArticleData;
use App\Data\Articles\ArticlesData;
use App\Data\Collections\CollectionDetailData;
use App\Data\Collections\CollectionFeaturedData;
use App\Data\Collections\CollectionTraitFilterData;
use App\Data\Collections\PopularCollectionData;
use App\Data\Gallery\GalleryNftData;
Expand All @@ -30,6 +31,7 @@
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Cache;
use Inertia\Inertia;
use Inertia\Response;
use Spatie\LaravelData\PaginatedDataCollection;
Expand All @@ -40,6 +42,16 @@ public function index(Request $request): Response|JsonResponse|RedirectResponse
{
$user = $request->user();

$featuredCollections = Collection::where('is_featured', true)
->withCount(['nfts'])
->get();

$featuredCollections->each(function (Collection $collection) {
$collection->cachedNfts = Cache::remember('featuredNftsForCollection'.$collection->id, 3600 * 12, function () use ($collection) {
return $collection->nfts()->inRandomOrder()->take(3)->get();
});
});

$currency = $user ? $user->currency() : CurrencyCode::USD;

$collectionQuery = $user ? $user->collections() : Collection::query();
Expand Down Expand Up @@ -70,6 +82,9 @@ public function index(Request $request): Response|JsonResponse|RedirectResponse
'collections' => PopularCollectionData::collection(
$collections->through(fn ($collection) => PopularCollectionData::fromModel($collection, $currency))
),
'featuredCollections' => $featuredCollections->map(function (Collection $collection) use ($user) {
return CollectionFeaturedData::fromModel($collection, $user ? $user->currency() : CurrencyCode::USD);
}),
]);
}

Expand Down
6 changes: 6 additions & 0 deletions app/Models/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* @property ?string $floor_price
* @property ?string $last_indexed_token_number
* @property ?string $image
* @property \Illuminate\Database\Eloquent\Collection<int,Nft> $cachedNfts
*
* @method BelongsToMany<Article> articlesWithCollections()
*/
Expand All @@ -44,6 +45,11 @@ class Collection extends Model

const DISCORD_URL = 'https://discord.gg/';

/**
* @var \Illuminate\Database\Eloquent\Collection<int, Nft>
*/
public $cachedNfts;

/**
* @var array<string>
*/
Expand Down
1 change: 1 addition & 0 deletions database/factories/CollectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function definition(): array
'image' => fake()->imageUrl(360, 360, 'animals', true),
'website' => fake()->url(),
]),
'is_featured' => fn () => random_int(0, 1),
];
}
}
9 changes: 9 additions & 0 deletions database/seeders/NftSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public function run(): void
'website' => $collectionArray['website'],
]),
'created_at' => fake()->dateTimeBetween('-2 months'),
'is_featured' => $collectionArray['is_featured'] ?? false,
'description' => $collectionArray['description'] ?? '',
]);

if (Feature::active(Features::Collections->value)) {
Expand Down Expand Up @@ -122,6 +124,8 @@ private function collections(): array
['Background', 'Cosmic Purple', TraitDisplayType::Property, 6, 0.06],
['Beak', 'Small', TraitDisplayType::Property, 4125, 42.15],
],
'is_featured' => true,
'description' => 'Moonbirds are a collection of 10,000 unique NFTs living on the Ethereum blockchain. Each Moonbird is algorithmically generated and has a unique combination of traits, including body, eyes, beak, wings, and background. Moonbirds are a Proof NFT project.',
],
[
// https://opensea.io/collection/timelessnfts
Expand Down Expand Up @@ -158,6 +162,8 @@ private function collections(): array
'traits' => [
['Background', 'Blue', TraitDisplayType::Property, 1636, 16.36],
],
'is_featured' => true,
'description' => 'Tubby Cats are a collection of 10,000 unique NFTs living on the Ethereum blockchain. Each Tubby Cat is algorithmically generated and has a unique combination of traits, including body, eyes, mouth, and background. Tubby Cats are a Proof NFT project.',
],
[
// https://opensea.io/collection/alphadogs
Expand All @@ -176,6 +182,7 @@ private function collections(): array
'traits' => [
['Background', 'Blue', TraitDisplayType::Property, 1636, 16.36],
],
'is_featured' => true,
],
[
// https://opensea.io/collection/devilvalley
Expand Down Expand Up @@ -210,6 +217,8 @@ private function collections(): array
'traits' => [
['Beak', 'Small', TraitDisplayType::Property, 4125, 42.15],
],
'is_featured' => true,
'description' => 'EDGEHOGS are a collection of 10,000 unique NFTs living on the Ethereum blockchain. Each EDGEHOG is algorithmically generated and has a unique combination of traits, including body, eyes, beak, wings, and background. EDGEHOGS are a Proof NFT project.',
],
[
// https://opensea.io/collection/cryptoadz-by-gremplin
Expand Down
1 change: 1 addition & 0 deletions lang/en/common.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
'n_hours' => '{0} :count hour|{1} :count hour|[2,*] :count hours',
'n_minutes' => '{0} :count minute|{1} :count minute|[2,*] :count minutes',
'report' => 'Report',
'nfts' => 'NFTs',
'n_nfts' => '{0}NFTs|{1}NFT|[2,*]NFTs',
'n_collections' => '{0}collections|{1}collection|[2,*]collections',
'back' => 'Back',
Expand Down
4 changes: 4 additions & 0 deletions lang/en/pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@
'notice_wait' => 'Please wait. You can refresh data every 15 minutes.',
'toast' => "We're updating information for your collection.",
],
'featured' => [
'title' => 'Featured Collections',
'button' => 'Explore Collection',
],
],

'nfts' => [
Expand Down
18 changes: 18 additions & 0 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,21 @@
.dark .auth-overlay-shadow {
box-shadow: 0px 15px 35px 0px rgba(18, 18, 19, 0.4);
}

.featured-collections-overlay {
background-image: linear-gradient(180deg, rgb(var(--theme-color-primary-50)) 47.38%, transparent 216.53%);
}

.dark .featured-collections-overlay {
background-image: linear-gradient(181deg, rgb(var(--theme-color-dark-800)) 41.03%, transparent 208.87%);
}

@media (min-width: 960px) {
.featured-collections-overlay {
background-image: linear-gradient(90deg, rgb(var(--theme-color-primary-50)) 55.67%, transparent 151.85%);
}

.dark .featured-collections-overlay {
background-image: linear-gradient(90deg, rgb(var(--theme-color-dark-800)) 55.67%, transparent 151.85%);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useRef } from "react";
import { useTranslation } from "react-i18next";
import { Img } from "@/Components/Image";
import { NetworkIcon } from "@/Components/Networks/NetworkIcon";
import { PriceChange } from "@/Components/PriceChange/PriceChange";
import { Tooltip } from "@/Components/Tooltip";
import { useIsTruncated } from "@/Hooks/useIsTruncated";
import { CollectionImageWithIcon } from "@/Pages/Collections/Components/CollectionImage";
import { FormatCrypto, FormatFiat } from "@/Utils/Currency";
import { isTruthy } from "@/Utils/is-truthy";

Expand All @@ -23,18 +22,12 @@ export const PopularCollectionName = ({
className="group relative h-11 w-full cursor-pointer md:h-12"
>
<div className="absolute flex w-full items-center space-x-4">
<div className="relative h-8 w-8 shrink-0 md:h-12 md:w-12">
<Img
wrapperClassName="aspect-square"
className="h-full w-full rounded-full object-cover"
src={collection.image}
isCircle
/>

<div className="absolute left-5 top-5 block h-4 w-4 rounded-full ring-4 ring-white dark:ring-theme-dark-900 md:left-8 md:top-8">
<NetworkIcon networkId={collection.chainId} />
</div>
</div>
<CollectionImageWithIcon
image={collection.image}
chainId={collection.chainId}
className="relative h-8 w-8 shrink-0 md:h-12 md:w-12"
wrapperClassName="aspect-square"
/>

<div className="break-word-legacy min-w-0 space-y-0.5 md:lg:space-y-0">
<Tooltip
Expand Down
12 changes: 12 additions & 0 deletions resources/js/Components/GridHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,16 @@ describe("GridHeader", () => {
expect(screen.getByTestId("GridHeader__title")).toHaveTextContent("Test Grid");
expect(screen.getByTestId("GridHeader__value")).toHaveTextContent("Missing Value");
});

it("should handle custom class name for wrapper", () => {
render(
<GridHeader
title="Test Grid"
value="Test Value"
wrapperClassName="test-wrapper"
/>,
);

expect(screen.getByTestId("GridHeader__wrapper")).toHaveClass("test-wrapper");
});
});
10 changes: 9 additions & 1 deletion resources/js/Components/GridHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ interface GridHeaderProperties {
title: string;
value: string | number | JSX.Element | null;
className?: string;
wrapperClassName?: string;
emptyValue?: string | null;
}

export const GridHeader = ({
title,
value,
className,
wrapperClassName,
emptyValue = null,
...properties
}: GridHeaderProperties): JSX.Element => {
Expand All @@ -29,7 +31,13 @@ export const GridHeader = ({
)}
{...properties}
>
<div className="mr-auto flex w-[100px] flex-col space-y-0.5 font-medium md:w-[110px] lg:w-auto">
<div
className={cn(
"mr-auto flex w-[100px] flex-col space-y-0.5 font-medium md:w-[110px] lg:w-auto",
wrapperClassName,
)}
data-testid="GridHeader__wrapper"
>
<span
data-testid="GridHeader__title"
className="text-sm leading-4.5 text-theme-secondary-500 dark:text-theme-dark-300 md:leading-5.5"
Expand Down
2 changes: 1 addition & 1 deletion resources/js/I18n/Locales/en.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import cn from "classnames";
import React from "react";
import { Img } from "@/Components/Image";
import { NetworkIcon } from "@/Components/Networks/NetworkIcon";

export const CollectionImageWithIcon = ({
image,
chainId,
className,
wrapperClassName,
networkClassName,
}: {
image: string | null;
chainId: App.Enums.Chain;
className?: string;
wrapperClassName?: string;
networkClassName?: string;
}): JSX.Element => (
<div className={className}>
<Img
wrapperClassName={wrapperClassName}
className="h-full w-full rounded-full object-cover"
src={image}
isCircle
/>

<div
className={cn(
"absolute left-5 top-5 block h-4 w-4 rounded-full ring-4 ring-white dark:ring-theme-dark-900 md:left-8 md:top-8",
networkClassName,
)}
>
<NetworkIcon networkId={chainId} />
</div>
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./CollectionImageWithIcon";
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import { Tooltip } from "@/Components/Tooltip";
import { useIsTruncated } from "@/Hooks/useIsTruncated";
import { isTruthy } from "@/Utils/is-truthy";

export const CollectionNft = ({ nft }: { nft: App.Data.Gallery.GalleryNftData }): JSX.Element => {
export const CollectionNft = ({
nft,
classNames,
}: {
nft: App.Data.Gallery.GalleryNftData;
classNames?: string;
}): JSX.Element => {
const { t } = useTranslation();

const nftTokenNumberReference = useRef<HTMLDivElement>(null);
Expand All @@ -22,7 +28,10 @@ export const CollectionNft = ({ nft }: { nft: App.Data.Gallery.GalleryNftData })
collection: nft.collectionSlug,
nft: nft.tokenNumber,
})}
className="transition-default group cursor-pointer rounded-xl border border-theme-secondary-300 p-2 ring-theme-primary-100 hover:ring-2 dark:border-theme-dark-700 dark:ring-theme-dark-700"
className={cn(
"transition-default group cursor-pointer rounded-xl border border-theme-secondary-300 p-2 ring-theme-primary-100 hover:ring-2 dark:border-theme-dark-700 dark:ring-theme-dark-700",
classNames,
)}
>
<span className="relative block">
<Img
Expand Down
Loading
Loading