Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
P
PaperChase
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
h703249754
PaperChase
Commits
7e291a8b
Commit
7e291a8b
authored
Apr 27, 2025
by
Aryan Patel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Aryan-4/27/25
parent
9cee33a8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
471 additions
and
385 deletions
+471
-385
project.pbxproj
ios/Runner.xcodeproj/project.pbxproj
+3
-3
book_detail_page.dart
lib/book_detail_page.dart
+127
-67
chat_page.dart
lib/chat_page.dart
+110
-51
inbox.dart
lib/inbox.dart
+201
-249
main.dart
lib/main.dart
+17
-11
mybooks.txt
lib/mybooks.txt
+0
-0
post.dart
lib/post.dart
+2
-0
profile.dart
lib/profile.dart
+11
-4
No files found.
ios/Runner.xcodeproj/project.pbxproj
View file @
7e291a8b
...
@@ -499,7 +499,7 @@
...
@@ -499,7 +499,7 @@
"$(inherited)"
,
"$(inherited)"
,
"@executable_path/Frameworks"
,
"@executable_path/Frameworks"
,
);
);
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting.paperchaseApp
;
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting
2
.paperchaseApp
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_VERSION
=
5.0
;
SWIFT_VERSION
=
5.0
;
...
@@ -682,7 +682,7 @@
...
@@ -682,7 +682,7 @@
"$(inherited)"
,
"$(inherited)"
,
"@executable_path/Frameworks"
,
"@executable_path/Frameworks"
,
);
);
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting.paperchaseApp
;
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting
2
.paperchaseApp
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_OPTIMIZATION_LEVEL
=
"-Onone"
;
SWIFT_OPTIMIZATION_LEVEL
=
"-Onone"
;
...
@@ -705,7 +705,7 @@
...
@@ -705,7 +705,7 @@
"$(inherited)"
,
"$(inherited)"
,
"@executable_path/Frameworks"
,
"@executable_path/Frameworks"
,
);
);
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting.paperchaseApp
;
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting
2
.paperchaseApp
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_VERSION
=
5.0
;
SWIFT_VERSION
=
5.0
;
...
...
lib/book_detail_page.dart
View file @
7e291a8b
...
@@ -2,20 +2,25 @@ import 'package:flutter/material.dart';
...
@@ -2,20 +2,25 @@ import 'package:flutter/material.dart';
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:paperchase_app/chat_page.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
import
'NavBar.dart'
;
class
BookDetailsPage
extends
StatelessWidget
{
class
BookDetailsPage
extends
StatelessWidget
{
final
Map
<
String
,
dynamic
>
book
;
final
Map
<
String
,
dynamic
>
book
;
final
String
bookId
;
const
BookDetailsPage
({
super
.
key
,
required
this
.
book
});
const
BookDetailsPage
({
super
.
key
,
required
this
.
book
,
required
this
.
bookId
});
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
bool
isDarkMode
=
Theme
.
of
(
context
).
brightness
==
Brightness
.
dark
;
final
bool
isDarkMode
=
Theme
.
of
(
context
).
brightness
==
Brightness
.
dark
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
isMyBook
=
currentUser
?.
uid
==
book
[
'userId'
];
final
isMyBook
=
currentUser
?.
uid
==
book
[
'userId'
];
final
title
=
book
[
'title'
]
??
'No title available'
;
final
title
=
book
[
'title'
]
??
'No title available'
;
final
author
=
book
[
'author'
]
??
'No author available'
;
final
author
=
book
[
'author'
]
??
'No author available'
;
final
isbn
=
book
[
'isbn'
]
??
'No ISBN available'
;
final
isbn
=
book
[
'isbn'
]
??
'No ISBN available'
;
...
@@ -28,6 +33,7 @@ class BookDetailsPage extends StatelessWidget {
...
@@ -28,6 +33,7 @@ class BookDetailsPage extends StatelessWidget {
return
Scaffold
(
return
Scaffold
(
appBar:
AppBar
(
appBar:
AppBar
(
automaticallyImplyLeading:
true
,
iconTheme:
IconThemeData
(
iconTheme:
IconThemeData
(
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
),
),
...
@@ -42,7 +48,7 @@ class BookDetailsPage extends StatelessWidget {
...
@@ -42,7 +48,7 @@ class BookDetailsPage extends StatelessWidget {
),
),
),
),
),
),
drawer:
const
NavBar
(),
body:
SingleChildScrollView
(
body:
SingleChildScrollView
(
child:
Padding
(
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
padding:
const
EdgeInsets
.
all
(
16.0
),
...
@@ -68,12 +74,31 @@ class BookDetailsPage extends StatelessWidget {
...
@@ -68,12 +74,31 @@ class BookDetailsPage extends StatelessWidget {
style:
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
bold
)),
style:
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
bold
)),
Text
(
description
,
style:
const
TextStyle
(
fontSize:
16
)),
Text
(
description
,
style:
const
TextStyle
(
fontSize:
16
)),
const
SizedBox
(
height:
24
),
const
SizedBox
(
height:
24
),
if
(!
isMyBook
&&
currentUser
!=
null
)
if
(
isMyBook
&&
currentUser
!=
null
)
SizedBox
(
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
=>
_confirmAndDeleteBook
(
context
,
bookId
),
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Colors
.
red
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
),
),
),
child:
const
Text
(
'Delete Book'
,
style:
TextStyle
(
fontSize:
18
,
color:
Colors
.
white
),
),
),
)
else
if
(!
isMyBook
&&
currentUser
!=
null
)
SizedBox
(
SizedBox
(
width:
double
.
infinity
,
width:
double
.
infinity
,
child:
ElevatedButton
(
child:
ElevatedButton
(
onPressed:
()
=>
onPressed:
()
=>
_contactSeller
(
context
,
book
[
'userId'
],
title
),
_contactSeller
(
context
,
book
,
bookId
),
style:
ElevatedButton
.
styleFrom
(
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
kPrimaryColor
,
backgroundColor:
kPrimaryColor
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
...
@@ -86,7 +111,9 @@ class BookDetailsPage extends StatelessWidget {
...
@@ -86,7 +111,9 @@ class BookDetailsPage extends StatelessWidget {
style:
TextStyle
(
fontSize:
18
,
color:
Colors
.
white
),
style:
TextStyle
(
fontSize:
18
,
color:
Colors
.
white
),
),
),
),
),
)
)
else
if
(
currentUser
==
null
)
else
if
(
currentUser
==
null
)
Center
(
Center
(
child:
TextButton
(
child:
TextButton
(
...
@@ -127,8 +154,9 @@ class BookDetailsPage extends StatelessWidget {
...
@@ -127,8 +154,9 @@ class BookDetailsPage extends StatelessWidget {
);
);
}
}
Future
<
void
>
_contactSeller
(
BuildContext
context
,
String
sellerId
,
String
bookTitle
)
async
{
Future
<
void
>
_contactSeller
(
BuildContext
context
,
Map
<
String
,
dynamic
>
book
,
String
bookId
)
async
{
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
if
(
currentUser
==
null
)
{
if
(
currentUser
==
null
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
...
@@ -137,69 +165,101 @@ class BookDetailsPage extends StatelessWidget {
...
@@ -137,69 +165,101 @@ class BookDetailsPage extends StatelessWidget {
return
;
return
;
}
}
try
{
final
sellerId
=
book
[
'userId'
];
// 📌 This is the user who posted the book
final
isBuyer
=
currentUser
.
uid
!=
sellerId
;
final
rolePrefix
=
isBuyer
?
'buyer'
:
'seller'
;
final
users
=
[
currentUser
.
uid
,
sellerId
]..
sort
();
final
users
=
[
currentUser
.
uid
,
sellerId
]..
sort
();
final
chatRoomId
=
users
.
join
(
'_'
)
;
final
chatRoomId
=
"
${rolePrefix}
_
${bookId}
_
${users.join('_')}
"
;
final
existingChat
=
await
FirebaseFirestore
.
instance
try
{
.
collection
(
'chats'
)
final
sellerDoc
=
await
FirebaseFirestore
.
instance
.
doc
(
chatRoomId
)
.
collection
(
'users'
)
.
doc
(
sellerId
)
.
get
();
.
get
();
final
sellerName
=
sellerDoc
.
exists
?
"
${sellerDoc['first_name']}
${sellerDoc['last_name']}
"
:
"Unknown Seller"
;
final
chatRef
=
FirebaseFirestore
.
instance
.
collection
(
'chats'
).
doc
(
chatRoomId
);
final
chatData
=
{
final
chatData
=
{
'users'
:
users
,
'users'
:
users
,
'bookId'
:
bookId
,
'bookTitle'
:
book
[
'title'
],
'lastMessage'
:
'Hi! Is this book still available?'
,
'lastMessage'
:
'Hi! Is this book still available?'
,
'lastMessageTime'
:
FieldValue
.
serverTimestamp
(),
'lastMessageTime'
:
FieldValue
.
serverTimestamp
(),
'bookTitle'
:
bookTitle
,
'createdAt'
:
FieldValue
.
serverTimestamp
(),
'createdAt'
:
FieldValue
.
serverTimestamp
(),
'participants'
:
{
'participants'
:
{
currentUser
.
uid
:
true
,
currentUser
.
uid
:
true
,
sellerId:
true
,
sellerId:
true
,
},
},
'sellerId'
:
sellerId
,
'buyerId'
:
isBuyer
?
currentUser
.
uid
:
null
,
// null if seller is messaging
};
};
final
existingChat
=
await
chatRef
.
get
();
if
(
existingChat
.
exists
)
{
if
(
existingChat
.
exists
)
{
await
FirebaseFirestore
.
instance
await
chatRef
.
update
({
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
.
update
({
'lastMessage'
:
chatData
[
'lastMessage'
],
'lastMessage'
:
chatData
[
'lastMessage'
],
'lastMessageTime'
:
chatData
[
'lastMessageTime'
],
'lastMessageTime'
:
chatData
[
'lastMessageTime'
],
});
});
}
else
{
}
else
{
await
FirebaseFirestore
.
instance
await
chatRef
.
set
(
chatData
);
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
.
set
(
chatData
);
}
}
await
FirebaseFirestore
.
instance
await
chatRef
.
collection
(
'messages'
).
add
({
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
.
collection
(
'messages'
)
.
add
({
'senderId'
:
currentUser
.
uid
,
'senderId'
:
currentUser
.
uid
,
'message'
:
'Hi! Is this book still available?'
,
'message'
:
'Hi! Is this book still available?'
,
'timestamp'
:
FieldValue
.
serverTimestamp
(),
'timestamp'
:
FieldValue
.
serverTimestamp
(),
'read'
:
false
,
});
});
if
(
context
.
mounted
)
{
Navigator
.
push
(
Navigator
.
pushReplacementNamed
(
context
,
'/inbox'
);
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
StrictChatPage
(
chatId:
chatRoomId
,
otherUserName:
sellerName
,
currentUserId:
currentUser
.
uid
,
sellerId:
sellerId
,
),
),
);
}
catch
(
e
)
{
debugPrint
(
'Error starting chat:
$e
'
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Chat started with the seller
'
)),
const
SnackBar
(
content:
Text
(
'Failed to contact seller. Please try again.
'
)),
);
);
}
}
}
catch
(
e
)
{
}
debugPrint
(
"Error contacting seller:
$e
"
);
void
_confirmAndDeleteBook
(
BuildContext
context
,
String
bookId
)
async
{
final
shouldDelete
=
await
showDialog
<
bool
>(
context:
context
,
builder:
(
context
)
=>
AlertDialog
(
title:
const
Text
(
'Confirm Deletion'
),
content:
const
Text
(
'Are you sure you want to delete this book?'
),
actions:
[
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
false
),
child:
const
Text
(
'Cancel'
)),
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
const
Text
(
'Delete'
)),
],
),
);
if
(
shouldDelete
==
true
)
{
await
FirebaseFirestore
.
instance
.
collection
(
'books'
).
doc
(
bookId
).
delete
();
if
(
context
.
mounted
)
{
if
(
context
.
mounted
)
{
Navigator
.
pop
(
context
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
const
SnackBar
(
content:
Text
(
'Book removed successfully'
)),
content:
Text
(
'Failed to contact seller. Please try again.'
)),
);
);
}
}
}
}
}
}
String
_formatPrice
(
dynamic
price
)
{
String
_formatPrice
(
dynamic
price
)
{
if
(
price
==
null
)
return
'0.00'
;
if
(
price
==
null
)
return
'0.00'
;
if
(
price
is
num
)
return
price
.
toStringAsFixed
(
2
);
if
(
price
is
num
)
return
price
.
toStringAsFixed
(
2
);
...
...
lib/chat_page.dart
View file @
7e291a8b
...
@@ -2,26 +2,23 @@ import 'package:flutter/foundation.dart';
...
@@ -2,26 +2,23 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:paperchase_app/book_detail_page.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
class
StrictChatPage
extends
StatefulWidget
{
class
StrictChatPage
extends
StatefulWidget
{
final
String
chatId
;
final
String
chatId
;
//final String bookId;
final
String
otherUserName
;
final
String
otherUserName
;
final
List
<
String
>
predefinedMessages
;
final
String
currentUserId
;
final
String
sellerId
;
const
StrictChatPage
({
const
StrictChatPage
({
Key
?
key
,
Key
?
key
,
required
this
.
chatId
,
required
this
.
chatId
,
required
this
.
otherUserName
,
required
this
.
otherUserName
,
this
.
predefinedMessages
=
const
[
required
this
.
currentUserId
,
"Is this still available?"
,
required
this
.
sellerId
,
"When can we meet?"
,
"I'll take it"
,
"Thanks!"
,
"Hello"
,
"Can you hold it for me?"
,
"What's your lowest price?"
,
],
})
:
super
(
key:
key
);
})
:
super
(
key:
key
);
@override
@override
...
@@ -32,6 +29,32 @@ class _StrictChatPageState extends State<StrictChatPage> {
...
@@ -32,6 +29,32 @@ class _StrictChatPageState extends State<StrictChatPage> {
final
ScrollController
_scrollController
=
ScrollController
();
final
ScrollController
_scrollController
=
ScrollController
();
final
TextEditingController
_messageController
=
TextEditingController
();
final
TextEditingController
_messageController
=
TextEditingController
();
String
?
_bookTitle
;
String
?
_bookTitle
;
String
?
_bookId
;
List
<
String
>
get
predefinedMessages
{
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
email
=
currentUser
?.
email
??
"your email"
;
if
(
widget
.
currentUserId
==
widget
.
sellerId
)
{
return
[
"Yes, it's still available."
,
"Thanks!"
,
"How about we meet this weekend?"
,
"That a deal!"
,
"Yes, I will hold it"
,
"Contact me at
$email
"
];
}
else
{
return
[
"Is this still available?"
,
"When can we meet?"
,
"I'll take it"
,
"Thanks!"
,
"Can you hold it for me?"
,
"Contact me at
$email
"
,
];
}
}
@override
@override
void
initState
()
{
void
initState
()
{
...
@@ -56,6 +79,7 @@ class _StrictChatPageState extends State<StrictChatPage> {
...
@@ -56,6 +79,7 @@ class _StrictChatPageState extends State<StrictChatPage> {
if
(
doc
.
exists
)
{
if
(
doc
.
exists
)
{
setState
(()
{
setState
(()
{
_bookTitle
=
doc
.
data
()?[
'bookTitle'
]
as
String
?;
_bookTitle
=
doc
.
data
()?[
'bookTitle'
]
as
String
?;
_bookId
=
doc
.
data
()?[
'bookId'
]
as
String
?;
});
});
}
}
}
catch
(
e
)
{
}
catch
(
e
)
{
...
@@ -111,7 +135,8 @@ class _StrictChatPageState extends State<StrictChatPage> {
...
@@ -111,7 +135,8 @@ class _StrictChatPageState extends State<StrictChatPage> {
final
backgroundColor2
=
isDarkMode
?
kLightBackground
:
kDarkBackground
;
final
backgroundColor2
=
isDarkMode
?
kLightBackground
:
kDarkBackground
;
final
textColor
=
isDarkMode
?
kDarkText
:
kLightText
;
final
textColor
=
isDarkMode
?
kDarkText
:
kLightText
;
final
textColor2
=
isDarkMode
?
kLightText
:
kDarkText
;
final
textColor2
=
isDarkMode
?
kLightText
:
kDarkText
;
final
messageBackgroundOther
=
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
];
final
messageBackgroundOther
=
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
];
return
Scaffold
(
return
Scaffold
(
appBar:
AppBar
(
appBar:
AppBar
(
...
@@ -176,7 +201,8 @@ class _StrictChatPageState extends State<StrictChatPage> {
...
@@ -176,7 +201,8 @@ class _StrictChatPageState extends State<StrictChatPage> {
final
text
=
data
[
'message'
]
as
String
?
??
''
;
final
text
=
data
[
'message'
]
as
String
?
??
''
;
final
senderId
=
data
[
'senderId'
]
as
String
?
??
''
;
final
senderId
=
data
[
'senderId'
]
as
String
?
??
''
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
isMe
=
currentUser
!=
null
&&
senderId
==
currentUser
.
uid
;
final
isMe
=
currentUser
!=
null
&&
senderId
==
currentUser
.
uid
;
return
Container
(
return
Container
(
margin:
const
EdgeInsets
.
symmetric
(
vertical:
4
),
margin:
const
EdgeInsets
.
symmetric
(
vertical:
4
),
...
@@ -187,7 +213,10 @@ class _StrictChatPageState extends State<StrictChatPage> {
...
@@ -187,7 +213,10 @@ class _StrictChatPageState extends State<StrictChatPage> {
children:
[
children:
[
Container
(
Container
(
constraints:
BoxConstraints
(
constraints:
BoxConstraints
(
maxWidth:
MediaQuery
.
of
(
context
).
size
.
width
*
0.75
,
maxWidth:
MediaQuery
.
of
(
context
)
.
size
.
width
*
0.75
,
),
),
padding:
const
EdgeInsets
.
symmetric
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
horizontal:
16
,
...
@@ -237,27 +266,57 @@ class _StrictChatPageState extends State<StrictChatPage> {
...
@@ -237,27 +266,57 @@ class _StrictChatPageState extends State<StrictChatPage> {
fontSize:
14
,
fontSize:
14
,
),
),
),
),
// Only show the Confirmed button if the current user is the seller
if
(
widget
.
currentUserId
==
widget
.
sellerId
)
SizedBox
(
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
=>
_confirmAndCompletePurchase
(
context
),
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Colors
.
lightGreenAccent
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
),
),
),
child:
const
Text
(
'Confirmed!'
,
style:
TextStyle
(
fontSize:
18
,
color:
kLightText
),
),
),
),
if
(
widget
.
currentUserId
==
widget
.
sellerId
)
const
SizedBox
(
height:
12
),
const
SizedBox
(
height:
12
),
Wrap
(
Wrap
(
spacing:
12
,
spacing:
12
,
runSpacing:
12
,
runSpacing:
12
,
children:
widget
.
predefinedMessages
.
map
((
msg
)
{
children:
predefinedMessages
.
map
((
msg
)
{
return
ActionChip
(
return
ActionChip
(
label:
Text
(
msg
),
label:
Text
(
msg
),
onPressed:
()
=>
_sendMessage
(
msg
),
onPressed:
()
=>
_sendMessage
(
msg
),
backgroundColor:
backgroundColor
,
backgroundColor:
backgroundColor
,
labelStyle:
TextStyle
(
labelStyle:
TextStyle
(
color:
textColor
,),
color:
textColor
,
),
);
);
}).
toList
(),
}).
toList
(),
),
),
const
SizedBox
(
height:
12
),
const
SizedBox
(
height:
12
),
],
],
),
),
),
),
],
],
),
),
);
);
}
}
void
_confirmAndCompletePurchase
(
BuildContext
context
)
async
{
await
FirebaseFirestore
.
instance
.
collection
(
'books'
).
doc
(
_bookId
).
delete
();
//await FirebaseFirestore.instance.collection('chats').doc(widget.chatId).collection('messages').doc().delete();
await
FirebaseFirestore
.
instance
.
collection
(
'chats'
).
doc
(
widget
.
chatId
).
delete
();
Navigator
.
pop
(
context
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Transaction completed!'
)),
);
}
}
}
\ No newline at end of file
lib/inbox.dart
View file @
7e291a8b
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:f
lutter/foundation.dart'
show
kDebugMode
;
import
'package:f
irebase_auth/firebase_auth.dart'
;
import
'
colors
.dart'
;
import
'
package:paperchase_app/NavBar
.dart'
;
import
'
NavBar
.dart'
;
import
'
package:paperchase_app/chat_page
.dart'
;
import
'
chat_page
.dart'
;
import
'
package:paperchase_app/colors
.dart'
;
enum
BookFilter
{
all
,
sold
,
bought
,
}
class
InboxPage
extends
StatefulWidget
{
class
InboxPage
extends
StatefulWidget
{
const
InboxPage
({
super
.
key
});
const
InboxPage
({
super
.
key
});
...
@@ -20,33 +14,10 @@ class InboxPage extends StatefulWidget {
...
@@ -20,33 +14,10 @@ class InboxPage extends StatefulWidget {
}
}
class
_InboxPageState
extends
State
<
InboxPage
>
{
class
_InboxPageState
extends
State
<
InboxPage
>
{
BookFilter
_currentFilter
=
BookFilter
.
all
;
String
_getFilterName
(
BookFilter
filter
)
{
switch
(
filter
)
{
case
BookFilter
.
all
:
return
'All Books'
;
case
BookFilter
.
sold
:
return
'Sold Books'
;
case
BookFilter
.
bought
:
return
'Bought Books'
;
}
}
Query
<
Map
<
String
,
dynamic
>>
_getFilteredQuery
(
String
userId
)
{
Query
<
Map
<
String
,
dynamic
>>
_getFilteredQuery
(
String
userId
)
{
final
baseQuery
=
FirebaseFirestore
.
instance
.
collection
(
'chats'
);
return
FirebaseFirestore
.
instance
switch
(
_currentFilter
)
{
.
collection
(
'chats'
)
case
BookFilter
.
all
:
.
where
(
'users'
,
arrayContains:
userId
);
return
baseQuery
.
where
(
'users'
,
arrayContains:
userId
);
case
BookFilter
.
sold
:
return
baseQuery
.
where
(
'users'
,
arrayContains:
userId
)
.
where
(
'sellerId'
,
isEqualTo:
userId
);
case
BookFilter
.
bought
:
return
baseQuery
.
where
(
'users'
,
arrayContains:
userId
)
.
where
(
'buyerId'
,
isEqualTo:
userId
);
}
}
}
@override
@override
...
@@ -61,9 +32,7 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -61,9 +32,7 @@ class _InboxPageState extends State<InboxPage> {
if
(
currentUser
==
null
)
{
if
(
currentUser
==
null
)
{
return
Scaffold
(
return
Scaffold
(
appBar:
AppBar
(
appBar:
AppBar
(
iconTheme:
IconThemeData
(
iconTheme:
IconThemeData
(
color:
textColor
),
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
),
title:
const
Text
(
title:
const
Text
(
"Inbox"
,
"Inbox"
,
style:
TextStyle
(
style:
TextStyle
(
...
@@ -74,7 +43,7 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -74,7 +43,7 @@ class _InboxPageState extends State<InboxPage> {
color:
kPrimaryColor
,
color:
kPrimaryColor
,
),
),
),
),
foregroundColor:
isDarkMode
?
kLightBackground
:
kDarkBackground
,
backgroundColor:
scaffoldColor
,
),
),
drawer:
const
NavBar
(),
drawer:
const
NavBar
(),
body:
Container
(
body:
Container
(
...
@@ -102,34 +71,13 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -102,34 +71,13 @@ class _InboxPageState extends State<InboxPage> {
),
),
),
),
),
),
bottomNavigationBar:
BottomNavigationBar
(
bottomNavigationBar:
_buildBottomNavigationBar
(
isDarkMode
,
textColor2
),
backgroundColor:
isDarkMode
?
kLightBackground
:
kDarkBackground
,
selectedItemColor:
kPrimaryColor
,
unselectedItemColor:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
currentIndex:
1
,
items:
const
[
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
home
),
label:
"Home"
),
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
add
),
label:
"Post"
),
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
mail
),
label:
"Inbox"
),
],
onTap:
(
index
)
{
if
(
index
==
0
)
{
Navigator
.
pushNamedAndRemoveUntil
(
context
,
'/'
,
(
route
)
=>
false
);
}
else
if
(
index
==
1
)
{
Navigator
.
pushNamed
(
context
,
'/post'
);
}
else
if
(
index
==
2
)
{
Navigator
.
pushNamed
(
context
,
'/inbox'
);
}
},
),
);
);
}
}
return
Scaffold
(
return
Scaffold
(
appBar:
AppBar
(
appBar:
AppBar
(
title:
Row
(
title:
const
Text
(
children:
[
const
Text
(
'Inbox'
,
'Inbox'
,
style:
TextStyle
(
style:
TextStyle
(
fontFamily:
'Impact'
,
fontFamily:
'Impact'
,
...
@@ -139,39 +87,6 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -139,39 +87,6 @@ class _InboxPageState extends State<InboxPage> {
color:
kPrimaryColor
,
color:
kPrimaryColor
,
),
),
),
),
const
SizedBox
(
width:
16
),
Expanded
(
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
),
decoration:
BoxDecoration
(
color:
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
],
borderRadius:
BorderRadius
.
circular
(
20
),
),
child:
DropdownButtonHideUnderline
(
child:
DropdownButton
<
BookFilter
>(
value:
_currentFilter
,
icon:
Icon
(
Icons
.
arrow_drop_down
,
color:
textColor
),
style:
TextStyle
(
color:
textColor
,
fontSize:
14
),
dropdownColor:
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
],
items:
BookFilter
.
values
.
map
((
filter
)
{
return
DropdownMenuItem
<
BookFilter
>(
value:
filter
,
child:
Text
(
_getFilterName
(
filter
)),
);
}).
toList
(),
onChanged:
(
BookFilter
?
newValue
)
{
if
(
newValue
!=
null
)
{
setState
(()
{
_currentFilter
=
newValue
;
});
}
},
),
),
),
),
],
),
backgroundColor:
scaffoldColor
,
backgroundColor:
scaffoldColor
,
iconTheme:
IconThemeData
(
color:
textColor2
),
iconTheme:
IconThemeData
(
color:
textColor2
),
),
),
...
@@ -183,26 +98,7 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -183,26 +98,7 @@ class _InboxPageState extends State<InboxPage> {
.
orderBy
(
'lastMessageTime'
,
descending:
true
)
.
orderBy
(
'lastMessageTime'
,
descending:
true
)
.
snapshots
(),
.
snapshots
(),
builder:
(
context
,
snapshot
)
{
builder:
(
context
,
snapshot
)
{
if
(
kDebugMode
)
{
print
(
'Current user ID in Inbox:
${currentUser.uid}
'
);
print
(
'Current filter:
${_getFilterName(_currentFilter)}
'
);
print
(
'Stream connection state:
${snapshot.connectionState}
'
);
if
(
snapshot
.
hasError
)
{
print
(
'Stream error:
${snapshot.error}
'
);
print
(
'Error stack trace:
${snapshot.stackTrace}
'
);
}
}
if
(
snapshot
.
hasError
)
{
if
(
snapshot
.
hasError
)
{
final
error
=
snapshot
.
error
.
toString
();
if
(
error
.
contains
(
'failed-precondition'
)
||
error
.
contains
(
'requires an index'
))
{
return
StreamBuilder
<
QuerySnapshot
>(
stream:
_getFilteredQuery
(
currentUser
.
uid
).
snapshots
(),
builder:
(
context
,
simpleSnapshot
)
{
return
_buildChatList
(
simpleSnapshot
,
currentUser
,
isDarkMode
,
textColor
);
},
);
}
return
Center
(
return
Center
(
child:
Text
(
child:
Text
(
'Error loading conversations:
${snapshot.error}
'
,
'Error loading conversations:
${snapshot.error}
'
,
...
@@ -219,24 +115,7 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -219,24 +115,7 @@ class _InboxPageState extends State<InboxPage> {
},
},
),
),
),
),
bottomNavigationBar:
BottomNavigationBar
(
bottomNavigationBar:
_buildBottomNavigationBar
(
isDarkMode
,
textColor2
),
backgroundColor:
scaffoldColor
,
selectedItemColor:
kPrimaryColor
,
unselectedItemColor:
textColor2
,
currentIndex:
2
,
items:
const
[
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
home
),
label:
'Home'
),
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
add
),
label:
'Post'
),
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
mail
),
label:
'Inbox'
),
],
onTap:
(
index
)
{
if
(
index
==
0
)
{
Navigator
.
pushReplacementNamed
(
context
,
'/'
);
}
else
if
(
index
==
1
)
{
Navigator
.
pushReplacementNamed
(
context
,
'/post'
);
}
},
),
);
);
}
}
...
@@ -250,22 +129,9 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -250,22 +129,9 @@ class _InboxPageState extends State<InboxPage> {
children:
[
children:
[
Icon
(
Icons
.
chat_bubble_outline
,
size:
64
,
color:
textColor
.
withOpacity
(
0.5
)),
Icon
(
Icons
.
chat_bubble_outline
,
size:
64
,
color:
textColor
.
withOpacity
(
0.5
)),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
Text
(
const
Text
(
'No conversations yet'
),
_currentFilter
==
BookFilter
.
all
?
'No conversations yet'
:
_currentFilter
==
BookFilter
.
sold
?
'No sold books conversations'
:
'No bought books conversations'
,
style:
TextStyle
(
fontSize:
18
,
color:
textColor
.
withOpacity
(
0.7
)),
),
const
SizedBox
(
height:
8
),
const
SizedBox
(
height:
8
),
Text
(
const
Text
(
'Browse books and contact sellers to start chatting'
),
_currentFilter
==
BookFilter
.
all
?
'Browse books and contact sellers to start chatting'
:
'No messages found for this filter'
,
style:
TextStyle
(
fontSize:
14
,
color:
textColor
.
withOpacity
(
0.5
)),
textAlign:
TextAlign
.
center
,
),
],
],
),
),
);
);
...
@@ -286,28 +152,15 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -286,28 +152,15 @@ class _InboxPageState extends State<InboxPage> {
itemBuilder:
(
context
,
index
)
{
itemBuilder:
(
context
,
index
)
{
final
chat
=
sortedChats
[
index
];
final
chat
=
sortedChats
[
index
];
final
data
=
chat
.
data
()
as
Map
<
String
,
dynamic
>;
final
data
=
chat
.
data
()
as
Map
<
String
,
dynamic
>;
final
chatId
=
chat
.
id
;
if
(
kDebugMode
)
{
// We'll determine real seller in FutureBuilder
print
(
'Chat data:
$data
'
);
final
bookId
=
data
[
'bookId'
]
as
String
?
??
''
;
}
final
lastMessage
=
data
[
'lastMessage'
]
as
String
?;
final
lastMessage
=
data
[
'lastMessage'
]
as
String
?;
final
lastMessageTime
=
(
data
[
'lastMessageTime'
]
as
Timestamp
?)?.
toDate
();
final
lastMessageTime
=
(
data
[
'lastMessageTime'
]
as
Timestamp
?)?.
toDate
();
final
bookTitle
=
data
[
'bookTitle'
]
as
String
?;
final
bookTitle
=
data
[
'bookTitle'
]
as
String
?;
final
usersList
=
(
data
[
'users'
]
as
List
?)?.
cast
<
String
>()
??
[];
final
usersList
=
(
data
[
'users'
]
as
List
?)?.
cast
<
String
>()
??
[];
String
otherUserId
;
String
otherUserId
=
usersList
.
firstWhere
((
id
)
=>
id
!=
currentUser
.
uid
,
orElse:
()
=>
'unknown'
);
try
{
otherUserId
=
usersList
.
firstWhere
(
(
id
)
=>
id
!=
currentUser
.
uid
,
orElse:
()
=>
'unknown'
,
);
}
catch
(
e
)
{
if
(
kDebugMode
)
{
print
(
'Error finding other user:
$e
'
);
}
otherUserId
=
'unknown'
;
}
return
FutureBuilder
<
DocumentSnapshot
>(
return
FutureBuilder
<
DocumentSnapshot
>(
future:
FirebaseFirestore
.
instance
.
collection
(
'users'
).
doc
(
otherUserId
).
get
(),
future:
FirebaseFirestore
.
instance
.
collection
(
'users'
).
doc
(
otherUserId
).
get
(),
...
@@ -319,6 +172,95 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -319,6 +172,95 @@ class _InboxPageState extends State<InboxPage> {
if
(
userName
.
isEmpty
)
userName
=
'Unknown User'
;
if
(
userName
.
isEmpty
)
userName
=
'Unknown User'
;
}
}
return
FutureBuilder
<
QuerySnapshot
>(
future:
FirebaseFirestore
.
instance
.
collection
(
'chats'
)
.
doc
(
chatId
)
.
collection
(
'messages'
)
.
orderBy
(
'timestamp'
,
descending:
false
)
.
limit
(
1
)
.
get
(),
builder:
(
context
,
messagesSnapshot
)
{
String
sellerId
=
''
;
String
buyerId
=
''
;
// Check who sent first message to determine buyer
if
(
messagesSnapshot
.
hasData
&&
messagesSnapshot
.
data
!.
docs
.
isNotEmpty
)
{
final
firstMessage
=
messagesSnapshot
.
data
!.
docs
.
first
;
final
firstMessageData
=
firstMessage
.
data
()
as
Map
<
String
,
dynamic
>;
buyerId
=
firstMessageData
[
'senderId'
]
as
String
?
??
''
;
// If buyer is first message sender, seller is the other user
sellerId
=
usersList
.
firstWhere
((
id
)
=>
id
!=
buyerId
,
orElse:
()
=>
''
);
}
else
{
// If no messages yet, use any seller ID from data if available
sellerId
=
data
[
'sellerId'
]
as
String
?
??
''
;
// If seller ID still not available, default to book owner from books collection
if
(
sellerId
.
isEmpty
&&
bookId
.
isNotEmpty
)
{
// This will be handled later in the next FutureBuilder
}
}
// Return placeholder while loading book data if necessary
if
(
sellerId
.
isEmpty
&&
bookId
.
isNotEmpty
)
{
return
FutureBuilder
<
DocumentSnapshot
>(
future:
FirebaseFirestore
.
instance
.
collection
(
'books'
).
doc
(
bookId
).
get
(),
builder:
(
context
,
bookSnapshot
)
{
if
(
bookSnapshot
.
hasData
&&
bookSnapshot
.
data
!.
exists
)
{
final
bookData
=
bookSnapshot
.
data
!.
data
()
as
Map
<
String
,
dynamic
>?
??
{};
sellerId
=
bookData
[
'userId'
]
as
String
?
??
''
;
}
// Now build the actual chat list item
return
_buildChatListItem
(
context
,
chatId
,
userName
,
currentUser
.
uid
,
sellerId
,
bookTitle
,
lastMessage
,
lastMessageTime
,
isDarkMode
,
textColor
,
);
},
);
}
return
_buildChatListItem
(
context
,
chatId
,
userName
,
currentUser
.
uid
,
sellerId
,
bookTitle
,
lastMessage
,
lastMessageTime
,
isDarkMode
,
textColor
,
);
},
);
},
);
},
);
}
Widget
_buildChatListItem
(
BuildContext
context
,
String
chatId
,
String
userName
,
String
currentUserId
,
String
sellerId
,
String
?
bookTitle
,
String
?
lastMessage
,
DateTime
?
lastMessageTime
,
bool
isDarkMode
,
Color
textColor
,
)
{
return
Card
(
return
Card
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
8
,
vertical:
4
),
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
8
,
vertical:
4
),
color:
isDarkMode
?
Colors
.
grey
[
900
]
:
Colors
.
white
,
color:
isDarkMode
?
Colors
.
grey
[
900
]
:
Colors
.
white
,
...
@@ -328,17 +270,10 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -328,17 +270,10 @@ class _InboxPageState extends State<InboxPage> {
context
,
context
,
MaterialPageRoute
(
MaterialPageRoute
(
builder:
(
context
)
=>
StrictChatPage
(
builder:
(
context
)
=>
StrictChatPage
(
chatId:
chat
.
i
d
,
chatId:
chatI
d
,
otherUserName:
userName
,
otherUserName:
userName
,
predefinedMessages:
const
[
currentUserId:
currentUserId
,
"Is this still available?"
,
sellerId:
sellerId
,
"When can we meet?"
,
"I'll take it"
,
"Thanks!"
,
"Hello"
,
"Can you hold it for me?"
,
"What's your lowest price?"
,
],
),
),
),
),
);
);
...
@@ -346,7 +281,7 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -346,7 +281,7 @@ class _InboxPageState extends State<InboxPage> {
leading:
CircleAvatar
(
leading:
CircleAvatar
(
backgroundColor:
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
],
backgroundColor:
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
],
child:
Text
(
child:
Text
(
userName
[
0
].
toUpperCase
()
,
userName
.
isNotEmpty
?
userName
[
0
].
toUpperCase
()
:
'?'
,
style:
TextStyle
(
style:
TextStyle
(
color:
textColor
,
color:
textColor
,
fontWeight:
FontWeight
.
bold
,
fontWeight:
FontWeight
.
bold
,
...
@@ -392,8 +327,25 @@ class _InboxPageState extends State<InboxPage> {
...
@@ -392,8 +327,25 @@ class _InboxPageState extends State<InboxPage> {
:
null
,
:
null
,
),
),
);
);
},
}
);
BottomNavigationBar
_buildBottomNavigationBar
(
bool
isDarkMode
,
Color
textColor2
)
{
return
BottomNavigationBar
(
backgroundColor:
isDarkMode
?
kLightBackground
:
kDarkBackground
,
selectedItemColor:
kPrimaryColor
,
unselectedItemColor:
textColor2
,
currentIndex:
2
,
items:
const
[
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
home
),
label:
'Home'
),
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
add
),
label:
'Post'
),
BottomNavigationBarItem
(
icon:
Icon
(
Icons
.
mail
),
label:
'Inbox'
),
],
onTap:
(
index
)
{
if
(
index
==
0
)
{
Navigator
.
pushReplacementNamed
(
context
,
'/'
);
}
else
if
(
index
==
1
)
{
Navigator
.
pushReplacementNamed
(
context
,
'/post'
);
}
},
},
);
);
}
}
...
...
lib/main.dart
View file @
7e291a8b
...
@@ -3,9 +3,6 @@ import 'package:flutter/material.dart';
...
@@ -3,9 +3,6 @@ import 'package:flutter/material.dart';
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:firebase_core/firebase_core.dart'
;
import
'package:firebase_core/firebase_core.dart'
;
import
'firebase_options.dart'
;
import
'firebase_options.dart'
;
import
'package:http/http.dart'
as
http
;
import
'dart:convert'
;
import
'package:url_launcher/url_launcher.dart'
;
import
'login.dart'
;
import
'login.dart'
;
import
'signup.dart'
;
import
'signup.dart'
;
import
'profile.dart'
;
import
'profile.dart'
;
...
@@ -14,7 +11,6 @@ import 'inbox.dart';
...
@@ -14,7 +11,6 @@ import 'inbox.dart';
import
'package:firebase_app_check/firebase_app_check.dart'
;
import
'package:firebase_app_check/firebase_app_check.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
import
'utils.dart'
;
import
'utils.dart'
;
import
'home.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'NavBar.dart'
;
import
'NavBar.dart'
;
import
'book_detail_page.dart'
;
import
'book_detail_page.dart'
;
...
@@ -185,7 +181,8 @@ class _HomePageState extends State<HomePage> {
...
@@ -185,7 +181,8 @@ class _HomePageState extends State<HomePage> {
}).
toList
();
}).
toList
();
setState
(()
{
setState
(()
{
_books
=
filteredBooks
.
map
((
doc
)
=>
doc
.
data
()).
toList
();
_books
=
filteredBooks
;
});
});
}
catch
(
e
)
{
}
catch
(
e
)
{
print
(
"Error searching books:
$e
"
);
print
(
"Error searching books:
$e
"
);
...
@@ -202,7 +199,8 @@ class _HomePageState extends State<HomePage> {
...
@@ -202,7 +199,8 @@ class _HomePageState extends State<HomePage> {
.
get
();
.
get
();
setState
(()
{
setState
(()
{
_books
=
snapshot
.
docs
.
map
((
doc
)
=>
doc
.
data
()).
toList
();
_books
=
snapshot
.
docs
;
});
});
}
catch
(
e
)
{
}
catch
(
e
)
{
print
(
"Error fetching recent books:
$e
"
);
print
(
"Error fetching recent books:
$e
"
);
...
@@ -357,21 +355,29 @@ String _filterBy = 'Latest Posted'; // Default filter option
...
@@ -357,21 +355,29 @@ String _filterBy = 'Latest Posted'; // Default filter option
itemCount:
_books
.
length
,
itemCount:
_books
.
length
,
itemBuilder:
(
context
,
index
)
{
itemBuilder:
(
context
,
index
)
{
final
book
=
_books
[
index
];
final
book
=
_books
[
index
];
final
bookId
=
book
.
id
;
final
data
=
book
.
data
()
as
Map
<
String
,
dynamic
>;
final
title
=
book
.
data
()[
'title'
]
??
"Unknown Title"
;
final
author
=
book
.
data
()[
'author'
]
??
"No author available"
;
final
thumbnail
=
book
.
data
()[
'imageUrl'
]
??
"https://via.placeholder.com/50"
;
final
price
=
book
.
data
()[
'price'
];
final
title
=
book
[
'title'
]
??
"Unknown Title"
;
final
author
=
book
[
'author'
]
??
"No author available"
;
final
thumbnail
=
book
[
'imageUrl'
]
??
"https://via.placeholder.com/50"
;
return
ListTile
(
return
ListTile
(
leading:
Image
.
network
(
thumbnail
,
width:
50
,
height:
50
,
fit:
BoxFit
.
cover
),
leading:
Image
.
network
(
thumbnail
,
width:
50
,
height:
50
,
fit:
BoxFit
.
cover
),
title:
Text
(
title
),
title:
Text
(
title
),
subtitle:
Text
(
author
),
subtitle:
Text
(
'
$author
-
\$
$price
-
${book.data()['condition'] ?? 'Condition not available'}
'
),
onTap:
()
{
onTap:
()
{
if
(
_isLoggedIn
)
{
if
(
_isLoggedIn
)
{
Navigator
.
push
(
Navigator
.
push
(
context
,
context
,
MaterialPageRoute
(
MaterialPageRoute
(
builder:
(
context
)
=>
BookDetailsPage
(
book:
book
),
// Pass book data
builder:
(
context
)
=>
BookDetailsPage
(
book:
book
.
data
()
as
Map
<
String
,
dynamic
>,
bookId:
bookId
),
// Pass book data
),
),
);
);
}
else
{
}
else
{
...
...
lib/mybooks.
dar
t
→
lib/mybooks.
tx
t
View file @
7e291a8b
File moved
lib/post.dart
View file @
7e291a8b
...
@@ -7,6 +7,7 @@ import 'package:image_picker/image_picker.dart';
...
@@ -7,6 +7,7 @@ import 'package:image_picker/image_picker.dart';
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:firebase_storage/firebase_storage.dart'
;
import
'package:firebase_storage/firebase_storage.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
import
'NavBar.dart'
;
class
PostBookPage
extends
StatefulWidget
{
class
PostBookPage
extends
StatefulWidget
{
@override
@override
...
@@ -137,6 +138,7 @@ Future<String?> uploadImageToImgur(File imageFile) async {
...
@@ -137,6 +138,7 @@ Future<String?> uploadImageToImgur(File imageFile) async {
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
bool
isDarkMode
=
Theme
.
of
(
context
).
brightness
==
Brightness
.
dark
;
final
bool
isDarkMode
=
Theme
.
of
(
context
).
brightness
==
Brightness
.
dark
;
return
Scaffold
(
return
Scaffold
(
drawer:
NavBar
(),
appBar:
AppBar
(
appBar:
AppBar
(
iconTheme:
IconThemeData
(
iconTheme:
IconThemeData
(
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
...
...
lib/profile.dart
View file @
7e291a8b
...
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
...
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:image_picker/image_picker.dart'
;
import
'package:image_picker/image_picker.dart'
;
import
'package:paperchase_app/book_detail_page.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
import
'dart:io'
;
import
'dart:io'
;
import
'colors.dart'
;
import
'colors.dart'
;
...
@@ -155,7 +156,9 @@ class ProfilePage extends StatelessWidget {
...
@@ -155,7 +156,9 @@ class ProfilePage extends StatelessWidget {
return
const
Center
(
child:
CircularProgressIndicator
());
return
const
Center
(
child:
CircularProgressIndicator
());
}
}
final
books
=
booksSnapshot
.
data
?.
docs
??
[];
final
books
=
booksSnapshot
.
data
!.
docs
;
if
(
books
.
isEmpty
)
{
if
(
books
.
isEmpty
)
{
return
Text
(
return
Text
(
...
@@ -182,6 +185,7 @@ class ProfilePage extends StatelessWidget {
...
@@ -182,6 +185,7 @@ class ProfilePage extends StatelessWidget {
physics:
const
NeverScrollableScrollPhysics
(),
physics:
const
NeverScrollableScrollPhysics
(),
itemCount:
books
.
length
,
itemCount:
books
.
length
,
itemBuilder:
(
context
,
index
)
{
itemBuilder:
(
context
,
index
)
{
final
doc
=
books
[
index
];
final
book
=
books
[
index
].
data
()
as
Map
<
String
,
dynamic
>;
final
book
=
books
[
index
].
data
()
as
Map
<
String
,
dynamic
>;
return
Card
(
return
Card
(
color:
isDarkMode
?
Colors
.
grey
[
900
]
:
Colors
.
white
,
color:
isDarkMode
?
Colors
.
grey
[
900
]
:
Colors
.
white
,
...
@@ -204,11 +208,14 @@ class ProfilePage extends StatelessWidget {
...
@@ -204,11 +208,14 @@ class ProfilePage extends StatelessWidget {
fontWeight:
FontWeight
.
bold
,
fontWeight:
FontWeight
.
bold
,
),
),
),
),
onTap:
()
{
onTap:
()
{
Navigator
.
push
Named
(
Navigator
.
push
(
context
,
context
,
'/book_details'
,
MaterialPageRoute
(
arguments:
books
[
index
].
id
,
builder:
(
context
)
=>
BookDetailsPage
(
book:
book
,
bookId:
doc
.
id
),
),
);
);
},
},
),
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment