Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 48 additions & 0 deletions FakeNFT.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@
79641C212DEF1ED7004C970D /* RatingList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79641C202DEF1ED7004C970D /* RatingList.swift */; };
79641C232DEF2081004C970D /* RatingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79641C222DEF2081004C970D /* RatingViewModel.swift */; };
79641C282DEF293A004C970D /* UserCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79641C272DEF293A004C970D /* UserCardView.swift */; };
796CB7962DF4269F0028631B /* CollectionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7952DF4269F0028631B /* CollectionRow.swift */; };
796CB7982DF42A620028631B /* UserNFTCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7972DF42A620028631B /* UserNFTCollection.swift */; };
796CB79A2DF42DB80028631B /* UserCollectionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7992DF42DB80028631B /* UserCollectionViewModel.swift */; };
796CB79C2DF43B000028631B /* NftInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB79B2DF43B000028631B /* NftInfo.swift */; };
796CB79E2DF43B770028631B /* NftInfoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB79D2DF43B770028631B /* NftInfoService.swift */; };
796CB7A02DF43C4E0028631B /* NftInfoRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB79F2DF43C4E0028631B /* NftInfoRequest.swift */; };
796CB7A22DF4658D0028631B /* LikesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7A12DF4658D0028631B /* LikesService.swift */; };
796CB7A42DF467060028631B /* LikesRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7A32DF467060028631B /* LikesRequest.swift */; };
796CB7A62DF46D500028631B /* UserLikes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7A52DF46D500028631B /* UserLikes.swift */; };
796CB7A82DF474330028631B /* UserOrdersService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7A72DF474330028631B /* UserOrdersService.swift */; };
796CB7AA2DF474A30028631B /* UserOrders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7A92DF474A30028631B /* UserOrders.swift */; };
796CB7AC2DF474F70028631B /* UserOrdersRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CB7AB2DF474F70028631B /* UserOrdersRequest.swift */; };
797AAE882DEF30E400745B0D /* UsersService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797AAE872DEF30E400745B0D /* UsersService.swift */; };
797AAE8A2DEF316100745B0D /* UsersRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797AAE892DEF316100745B0D /* UsersRequest.swift */; };
79D0C7532DE396AC00D53241 /* Tab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79D0C7522DE396AC00D53241 /* Tab.swift */; };
Expand Down Expand Up @@ -108,6 +120,18 @@
79641C202DEF1ED7004C970D /* RatingList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RatingList.swift; sourceTree = "<group>"; };
79641C222DEF2081004C970D /* RatingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RatingViewModel.swift; path = FakeNFT/RatingViewModel.swift; sourceTree = SOURCE_ROOT; };
79641C272DEF293A004C970D /* UserCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCardView.swift; sourceTree = "<group>"; };
796CB7952DF4269F0028631B /* CollectionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionRow.swift; sourceTree = "<group>"; };
796CB7972DF42A620028631B /* UserNFTCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNFTCollection.swift; sourceTree = "<group>"; };
796CB7992DF42DB80028631B /* UserCollectionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UserCollectionViewModel.swift; path = FakeNFT/UserCollectionViewModel.swift; sourceTree = SOURCE_ROOT; };
796CB79B2DF43B000028631B /* NftInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NftInfo.swift; sourceTree = "<group>"; };
796CB79D2DF43B770028631B /* NftInfoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NftInfoService.swift; sourceTree = "<group>"; };
796CB79F2DF43C4E0028631B /* NftInfoRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NftInfoRequest.swift; sourceTree = "<group>"; };
796CB7A12DF4658D0028631B /* LikesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikesService.swift; sourceTree = "<group>"; };
796CB7A32DF467060028631B /* LikesRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikesRequest.swift; sourceTree = "<group>"; };
796CB7A52DF46D500028631B /* UserLikes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLikes.swift; sourceTree = "<group>"; };
796CB7A72DF474330028631B /* UserOrdersService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserOrdersService.swift; sourceTree = "<group>"; };
796CB7A92DF474A30028631B /* UserOrders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserOrders.swift; sourceTree = "<group>"; };
796CB7AB2DF474F70028631B /* UserOrdersRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserOrdersRequest.swift; sourceTree = "<group>"; };
797AAE872DEF30E400745B0D /* UsersService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersService.swift; sourceTree = "<group>"; };
797AAE892DEF316100745B0D /* UsersRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersRequest.swift; sourceTree = "<group>"; };
79D0C7522DE396AC00D53241 /* Tab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tab.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -244,6 +268,9 @@
0C79EE6B2A76DE2E00EE90EA /* ServicesAssemly.swift */,
558E39E62C68CE0900FB86AC /* NftService.swift */,
797AAE872DEF30E400745B0D /* UsersService.swift */,
796CB79D2DF43B770028631B /* NftInfoService.swift */,
796CB7A12DF4658D0028631B /* LikesService.swift */,
796CB7A72DF474330028631B /* UserOrdersService.swift */,
0CFCB73F2A78002A0009A829 /* ExamplePutService.swift */,
);
path = Services;
Expand Down Expand Up @@ -295,6 +322,9 @@
children = (
0CFCB7412A78013E0009A829 /* Nft.swift */,
79641C172DEF1D33004C970D /* User.swift */,
796CB79B2DF43B000028631B /* NftInfo.swift */,
796CB7A52DF46D500028631B /* UserLikes.swift */,
796CB7A92DF474A30028631B /* UserOrders.swift */,
);
path = Network;
sourceTree = "<group>";
Expand All @@ -306,6 +336,9 @@
3FC8C39029D2453B0081F015 /* ExamplePutRequest.swift */,
0C79EE602A76DCD600EE90EA /* NftByIdRequest.swift */,
797AAE892DEF316100745B0D /* UsersRequest.swift */,
796CB79F2DF43C4E0028631B /* NftInfoRequest.swift */,
796CB7A32DF467060028631B /* LikesRequest.swift */,
796CB7AB2DF474F70028631B /* UserOrdersRequest.swift */,
);
path = Requests;
sourceTree = "<group>";
Expand Down Expand Up @@ -354,6 +387,7 @@
isa = PBXGroup;
children = (
79641C1B2DEF1E17004C970D /* RatingRow.swift */,
796CB7952DF4269F0028631B /* CollectionRow.swift */,
);
path = Row;
sourceTree = "<group>";
Expand All @@ -362,6 +396,7 @@
isa = PBXGroup;
children = (
79641C202DEF1ED7004C970D /* RatingList.swift */,
796CB7972DF42A620028631B /* UserNFTCollection.swift */,
);
path = List;
sourceTree = "<group>";
Expand All @@ -388,6 +423,7 @@
children = (
79641C222DEF2081004C970D /* RatingViewModel.swift */,
79D9E0432DF03392005D5DB1 /* UserCardViewModel.swift */,
796CB7992DF42DB80028631B /* UserCollectionViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand Down Expand Up @@ -591,35 +627,47 @@
buildActionMask = 2147483647;
files = (
79641C182DEF1D33004C970D /* User.swift in Sources */,
796CB7A62DF46D500028631B /* UserLikes.swift in Sources */,
3F478ECF29DB474E00F6D39E /* Colors.swift in Sources */,
3F478ED129DB476500F6D39E /* Fonts.swift in Sources */,
793BCC992DEF1B8800DF1252 /* StatisticsConstants.swift in Sources */,
3FC8C39329D246BA0081F015 /* DateFormatters+Presets.swift in Sources */,
79641C1E2DEF1E63004C970D /* UserAvatar.swift in Sources */,
796CB79E2DF43B770028631B /* NftInfoService.swift in Sources */,
793BCC972DEF12F500DF1252 /* RatingView.swift in Sources */,
796CB7A02DF43C4E0028631B /* NftInfoRequest.swift in Sources */,
0CFCB7422A78013E0009A829 /* Nft.swift in Sources */,
796CB7962DF4269F0028631B /* CollectionRow.swift in Sources */,
79D0C7592DE39C3800D53241 /* NavigationBarStyle.swift in Sources */,
79D0C75D2DE39F5900D53241 /* LoadingView.swift in Sources */,
797AAE8A2DEF316100745B0D /* UsersRequest.swift in Sources */,
0CF2C2DD2A783CE600FDC837 /* ErrorView.swift in Sources */,
79D9E0402DF0316F005D5DB1 /* ShowWebViewButton.swift in Sources */,
79641C212DEF1ED7004C970D /* RatingList.swift in Sources */,
796CB79A2DF42DB80028631B /* UserCollectionViewModel.swift in Sources */,
796CB7AC2DF474F70028631B /* UserOrdersRequest.swift in Sources */,
5019A4CE2DE1E899009A5AF8 /* FakeNftApp.swift in Sources */,
79D9E0442DF03392005D5DB1 /* UserCardViewModel.swift in Sources */,
79641C1C2DEF1E17004C970D /* RatingRow.swift in Sources */,
79641C282DEF293A004C970D /* UserCardView.swift in Sources */,
0CFCB7402A78002A0009A829 /* ExamplePutService.swift in Sources */,
797AAE882DEF30E400745B0D /* UsersService.swift in Sources */,
796CB7AA2DF474A30028631B /* UserOrders.swift in Sources */,
3F6806D529CBBEC700B4F915 /* NetworkTask.swift in Sources */,
558E39E72C68CE0A00FB86AC /* NftService.swift in Sources */,
796CB7A42DF467060028631B /* LikesRequest.swift in Sources */,
0C79EE6C2A76DE2E00EE90EA /* ServicesAssemly.swift in Sources */,
793BCC902DEEF06800DF1252 /* WebViewRepresentable.swift in Sources */,
796CB7A82DF474330028631B /* UserOrdersService.swift in Sources */,
79D0C7532DE396AC00D53241 /* Tab.swift in Sources */,
0CFCB74E2A7817DC0009A829 /* NftStorage.swift in Sources */,
3FC8C39129D2453B0081F015 /* ExamplePutRequest.swift in Sources */,
5019A4D02DE1E8E0009A5AF8 /* ContentView.swift in Sources */,
796CB79C2DF43B000028631B /* NftInfo.swift in Sources */,
79D9E0422DF03260005D5DB1 /* ShowCollectionButton.swift in Sources */,
796CB7A22DF4658D0028631B /* LikesService.swift in Sources */,
79641C232DEF2081004C970D /* RatingViewModel.swift in Sources */,
796CB7982DF42A620028631B /* UserNFTCollection.swift in Sources */,
0C79EE612A76DCD600EE90EA /* NftByIdRequest.swift in Sources */,
79D9E0472DF03E41005D5DB1 /* UserCollectionView.swift in Sources */,
3F6806D329CBBE9600B4F915 /* NetworkRequest.swift in Sources */,
Expand Down
16 changes: 8 additions & 8 deletions FakeNFT/Docs/Statistics.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@
## Module 3:

#### Верстка
- Экран коллекции пользователя (est: 20 минут; fact: x часов).
- Ячейка с информацией об NFT (est: 1 час; fact: x часов).
- Экран коллекции пользователя (est: 20 минут; fact: 20 минут).
- Ячейка с информацией об NFT (est: 1 час; fact: 50 минут).

#### Логика
- Обработка нажатия на сердечко (est: 1 час; fact: x часов).
- Обработка нажатия на кнопку корзины (добавить/удалить NFT) (est: 1 час; fact: x часов).
- Обработка нажатия на сердечко (est: 1 час; fact: 1 час).
- Обработка нажатия на кнопку корзины (добавить/удалить NFT) (est: 1 час; fact: 1 час).

#### Работа с сетью
- Загрузка и отображение изображений NFT (est: 1 час; fact: x часов).
- Запрос на добавление/удаление лайка (est: 1 час; fact: x часов).
- Запрос на добавление NFT в корзину (est: 1 час; fact: x часов).
- Запрос на удаление NFT из корзины (est: 1 час; fact: x часов).
- Загрузка и отображение изображений NFT (est: 1 час; fact: 30 минут).
- Запрос на добавление/удаление лайка (est: 1 час; fact: 2 часа).
- Запрос на добавление NFT в корзину (est: 1 час; fact: 30 минут).
- Запрос на удаление NFT из корзины (est: 1 час; fact: 30 минут).
16 changes: 16 additions & 0 deletions FakeNFT/Models/Network/NftInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// NftInfo.swift
// FakeNFT
//
// Created by Anastasia on 07.06.2025.
//

import Foundation

struct NftInfo: Decodable, Hashable {
let id: String
let name: String
let images: [String]
let rating: Int
let price: Double
}
12 changes: 12 additions & 0 deletions FakeNFT/Models/Network/UserLikes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// UserLikes.swift
// FakeNFT
//
// Created by Anastasia on 07.06.2025.
//

import Foundation

struct UserLikes: Decodable {
let likes: [String]
}
12 changes: 12 additions & 0 deletions FakeNFT/Models/Network/UserOrders.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// UserOrders.swift
// FakeNFT
//
// Created by Anastasia on 07.06.2025.
//

import Foundation

struct UserOrders: Decodable {
let nfts: [String]
}
57 changes: 57 additions & 0 deletions FakeNFT/Scenes /Statistics/Common/List/UserNFTCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// NFTCollection.swift
// FakeNFT
//
// Created by Anastasia on 07.06.2025.
//

import SwiftUI

struct UserNFTCollection: View {

let nftInfo: [NftInfo]
let userLikes: UserLikes
let userOrders: UserOrders
var likeTapHandler: (NftInfo) -> Void
var cartTapHandler: (NftInfo) -> Void

let columns = [
GridItem(.flexible(), alignment: .top),
GridItem(.flexible(), alignment: .top),
GridItem(.flexible(), alignment: .top)
]

var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(nftInfo, id: \.self) { nft in
CollectionRow(
nft: nft,
userLikes: userLikes,
userOrders: userOrders,
likeTapHandler: likeTapHandler,
cartTapHandler: cartTapHandler
)
}
}
.padding(.horizontal)
}
}
}

#Preview {
UserNFTCollection(
nftInfo: [
NftInfo(
id: "",
name: "",
images: [""],
rating: 1,
price: 3.98
)],
userLikes: UserLikes(likes: []),
userOrders: UserOrders(nfts: []),
likeTapHandler: {_ in },
cartTapHandler: {_ in }
)
}
118 changes: 118 additions & 0 deletions FakeNFT/Scenes /Statistics/Common/Row/CollectionRow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// CollectionRow.swift
// FakeNFT
//
// Created by Anastasia on 07.06.2025.
//

import SwiftUI

struct CollectionRow: View {

// MARK: - Properties

let nft: NftInfo
let userLikes: UserLikes
let userOrders: UserOrders
var likeTapHandler: (NftInfo) -> Void
var cartTapHandler: (NftInfo) -> Void

// MARK: - Content

var body: some View {
content
}

// MARK: - View

private var content: some View {
VStack(spacing: .zero) {
image
ratingView
.padding(.top, StatisticsConstants.anchorSmall)
nftInfo
.padding(.top, StatisticsConstants.rowAnchorSmall)
.padding(.bottom, StatisticsConstants.rowAnchorMedium)
}
.frame(width: StatisticsConstants.collectionRowSize)
}

private var image: some View {
ZStack(alignment: .top) {
AsyncImage(url: URL(string: nft.images.first ?? "")) { phase in
switch phase {
case .success(let image):
image
.resizable()
.frame(
width: StatisticsConstants.collectionRowSize,
height: StatisticsConstants.collectionRowSize
)
.aspectRatio(contentMode: .fill)
.clipShape(.rect(cornerRadius: StatisticsConstants.cornerRadiusSmall))
case .failure, .empty:
RoundedRectangle(cornerRadius: StatisticsConstants.cornerRadiusSmall)
.fill(Color.lightGrayDay)
.frame(
width: StatisticsConstants.collectionRowSize,
height: StatisticsConstants.collectionRowSize
)
default:
EmptyView()
}
}
HStack {
Spacer()
Button {
likeTapHandler(nft)
} label: {
Image(userLikes.likes.contains(nft.id) ? "likeActive" : "likeNoActive")
}
}
}
}

private var ratingView: some View {
HStack(spacing: 2) {
let rating = nft.rating
ForEach(1..<6) { index in
Image(index <= rating ? "starActive" : "starNoActive")
}
Spacer()
}
}

private var nftInfo: some View {
HStack(spacing: .zero) {
VStack(alignment: .leading) {
Text(nft.name)
.font(.bold17)
Text("\(nft.price, specifier: "%.2f") ETH")
.font(.medium10)
}
.foregroundStyle(Color.blackDay)
Spacer()
Button {
cartTapHandler(nft)
} label: {
Image(userOrders.nfts.contains(nft.id) ? "cartDelete" : "cartAdd")
}
}
}
}

#Preview {
CollectionRow(
nft: NftInfo(
id: "",
name: "",
images: [""],
rating: 4,
price: 1.79
),
userLikes: UserLikes(likes: []),
userOrders: UserOrders(nfts: []),
likeTapHandler: {_ in },
cartTapHandler: {_ in}
)
}
Loading