diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss
index 8d3cd19..f4381ae 100644
--- a/src/app/profile/profile.component.scss
+++ b/src/app/profile/profile.component.scss
@@ -19,8 +19,8 @@
margin-right: 10px;
grid-column: 1;
img {
- width: 50px;
- height: 50px;
+ width: 100px;
+ height: 100px;
background-color: #ddd;
border-radius: 50%;
margin-right: 10px;
@@ -59,6 +59,7 @@
}
li {
padding: 10px 0;
+ cursor: pointer;
}
}
@media (max-width: 768px) {
diff --git a/src/app/profile/profile.component.ts b/src/app/profile/profile.component.ts
index 5a5d6d4..9700329 100644
--- a/src/app/profile/profile.component.ts
+++ b/src/app/profile/profile.component.ts
@@ -1,8 +1,8 @@
import { Component, inject } from '@angular/core';
import { Auth } from '@angular/fire/auth';
-import { FirestoreService } from '../shared/services/firestore/firestore.service';
import { CommonModule } from '@angular/common';
-import { RouterModule, RouterOutlet } from '@angular/router';
+import { RouterModule } from '@angular/router';
+import { UserRelationService } from '../shared/services/firestore/user-relation.service';
@Component({
selector: 'app-profile',
@@ -14,13 +14,16 @@ import { RouterModule, RouterOutlet } from '@angular/router';
export class ProfileComponent {
private readonly auth: Auth = inject(Auth);
- private readonly firestore: FirestoreService = inject(FirestoreService);
+ private readonly userRelations: UserRelationService = inject(UserRelationService);
userDetails: any = {};
ngOnInit() {
if (this.auth.currentUser) {
-
- this.firestore.getUserById(this.auth.currentUser.uid).subscribe((data) => this.userDetails = data);
+ this.userRelations.getUserById(this.auth.currentUser.uid).subscribe((data) => this.userDetails = data);
}
}
+ handleImageError(event: any) {
+ (event.target as HTMLImageElement).style.display = 'none';
+ }
+
}
diff --git a/src/app/shared/components/other-user/other-user.component.html b/src/app/shared/components/other-user/other-user.component.html
new file mode 100644
index 0000000..4756d1d
--- /dev/null
+++ b/src/app/shared/components/other-user/other-user.component.html
@@ -0,0 +1,34 @@
+@if(user.uid!==currentUserId) {
+
+
+
+
+
+
+
{{ user.name }}
+
{{ user.email }}
+
+
Followers: {{ user.followerCount }}
+
+
+
+ @if (user.isFollowing) {
+
+ } @else{
+
+ }
+
+
+}
diff --git a/src/app/shared/components/other-user/other-user.component.scss b/src/app/shared/components/other-user/other-user.component.scss
new file mode 100644
index 0000000..1cc7102
--- /dev/null
+++ b/src/app/shared/components/other-user/other-user.component.scss
@@ -0,0 +1,61 @@
+@import "../../../../partials/buttons";
+@import "../../../../partials/variables";
+
+.user-item {
+ background: #fff;
+ border-bottom: 0.5px solid rgb(190, 190, 190);
+ margin-bottom: 20px;
+ padding: 20px 20px 0 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.user-followers {
+ color: #999;
+ font-size: 0.9em;
+}
+
+.user-followers {
+ color: #999;
+ font-size: 0.9em;
+}
+
+.author-info {
+ display: flex;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.author-image {
+ width: 50px;
+ height: 50px;
+ background-color: #ddd;
+ border-radius: 50%;
+ margin-right: 10px;
+ img {
+ width: 50px;
+ height: 50px;
+ background-color: #ddd;
+ border-radius: 50%;
+ margin-right: 10px;
+ }
+}
+
+.author-details {
+ h3 {
+ margin: 0;
+ }
+ .time-stamp {
+ color: #999;
+ font-size: 0.9em;
+ }
+}
+
+.custom-button {
+ @include write-button($accent-color);
+}
+
+.custom-following-button {
+ @include following-button($accent-color);
+}
diff --git a/src/app/profile/posts/posts.component.spec.ts b/src/app/shared/components/other-user/other-user.component.spec.ts
similarity index 53%
rename from src/app/profile/posts/posts.component.spec.ts
rename to src/app/shared/components/other-user/other-user.component.spec.ts
index 0f677e2..110b055 100644
--- a/src/app/profile/posts/posts.component.spec.ts
+++ b/src/app/shared/components/other-user/other-user.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { PostsComponent } from './posts.component';
+import { OtherUserComponent } from './other-user.component';
-describe('PostsComponent', () => {
- let component: PostsComponent;
- let fixture: ComponentFixture
;
+describe('OtherUserComponent', () => {
+ let component: OtherUserComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [PostsComponent]
+ imports: [OtherUserComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(PostsComponent);
+ fixture = TestBed.createComponent(OtherUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/shared/components/other-user/other-user.component.ts b/src/app/shared/components/other-user/other-user.component.ts
new file mode 100644
index 0000000..f4d6af0
--- /dev/null
+++ b/src/app/shared/components/other-user/other-user.component.ts
@@ -0,0 +1,28 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { User } from '../../types/User';
+
+@Component({
+ selector: 'OtherUser',
+ standalone: true,
+ imports: [],
+ templateUrl: './other-user.component.html',
+ styleUrl: './other-user.component.scss'
+})
+export class OtherUserComponent {
+
+ @Input() currentUserId?: string = "";
+ @Input() user!: any;
+ @Output() follow = new EventEmitter();
+ @Output() unfollow = new EventEmitter();
+
+ handleImageError(event: any) {
+ (event.target as HTMLImageElement).style.display = 'none';
+ }
+ followUser(): void {
+ this.follow.emit(this.user);
+ }
+
+ unfollowUser(): void {
+ this.unfollow.emit(this.user.uid);
+ }
+}
diff --git a/src/app/shared/components/snackbar/snackbar.component.ts b/src/app/shared/components/snackbar/snackbar.component.ts
index 275a7d2..0c4e6ad 100644
--- a/src/app/shared/components/snackbar/snackbar.component.ts
+++ b/src/app/shared/components/snackbar/snackbar.component.ts
@@ -1,25 +1,24 @@
-// snackbar.component.ts
import { Component, Input } from '@angular/core';
@Component({
- selector: 'app-snackbar',
- standalone: true,
- template: `
+ selector: 'Snackbar',
+ standalone: true,
+ template: `
@if (show) {
{{ message }}
}
`,
- styleUrl: './snackbar.component.scss'
+ styleUrl: './snackbar.component.scss'
})
export class SnackbarComponent {
- @Input() message: string = '';
- show: boolean = false;
+ @Input() message: string = '';
+ show: boolean = false;
- display(message: string, duration: number = 3000) {
- this.message = message;
- this.show = true;
- setTimeout(() => this.show = false, duration);
- }
+ display(message: string, duration: number = 3000) {
+ this.message = message;
+ this.show = true;
+ setTimeout(() => this.show = false, duration);
+ }
}
diff --git a/src/app/shared/services/auth/auth.service.ts b/src/app/shared/services/auth/auth.service.ts
index 778f278..01961a4 100644
--- a/src/app/shared/services/auth/auth.service.ts
+++ b/src/app/shared/services/auth/auth.service.ts
@@ -1,8 +1,9 @@
import { Injectable, inject } from '@angular/core';
-import { Auth, GoogleAuthProvider, signInWithPopup } from '@angular/fire/auth';
+import { Auth, GoogleAuthProvider, User, signInWithPopup } from '@angular/fire/auth';
import { Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { SnackbarService } from '../../components/snackbar/snackbar.service';
+import { Firestore, doc, getDoc } from '@angular/fire/firestore';
@Injectable({
providedIn: 'root'
@@ -12,6 +13,7 @@ export class AuthService {
private readonly auth: Auth = inject(Auth);
private readonly router: Router = inject(Router);
private readonly snackbarService: SnackbarService = inject(SnackbarService);
+ private readonly firestore: Firestore = inject(Firestore);
async signInWithGoogle() {
try {
@@ -28,14 +30,17 @@ export class AuthService {
return of(this.auth.currentUser !== null);
}
- getCurrentUserDetails() {
+
+ async getCurrentUserDetails(): Promise {
if (this.auth.currentUser) {
- const user = this.auth.currentUser
- return {
- uid: user.uid,
- email: user.email,
- photoUrl: user.photoURL,
- name: user.displayName
+ const userId = this.auth.currentUser.uid;
+ const userRef = doc(this.firestore, `users/${userId}`);
+ const docSnapshot = await getDoc(userRef);
+
+ if (docSnapshot.exists()) {
+ return docSnapshot.data() as User;
+ } else {
+ return null;
}
}
return null;
diff --git a/src/app/shared/services/firestore/firestore.service.ts b/src/app/shared/services/firestore/firestore.service.ts
index 168a9e8..e304b87 100644
--- a/src/app/shared/services/firestore/firestore.service.ts
+++ b/src/app/shared/services/firestore/firestore.service.ts
@@ -1,7 +1,7 @@
import { Injectable, inject } from '@angular/core';
-import { DocumentData, Firestore, addDoc, collection, collectionData, deleteDoc, doc, docData, increment, orderBy, query, setDoc, updateDoc, where } from '@angular/fire/firestore';
+import { DocumentData, Firestore, addDoc, collection, collectionData, deleteDoc, doc, docData, getDocs, increment, orderBy, query, setDoc, updateDoc, where } from '@angular/fire/firestore';
import { Post } from '../../types/Post';
-import { Observable, map } from 'rxjs';
+import { Observable, map, switchMap } from 'rxjs';
import { User } from '../../types/User';
import { AuthService } from '../auth/auth.service';
@@ -11,14 +11,13 @@ import { AuthService } from '../auth/auth.service';
export class FirestoreService {
private readonly firestore: Firestore = inject(Firestore);
- private readonly auth: AuthService = inject(AuthService);
- addUserDetails(userId: string, userDetails: any) {
+ addUserDetails(userId: string, userDetails: any): Promise {
const userDocRef = doc(this.firestore, `users/${userId}`);
return setDoc(userDocRef, userDetails);
}
- async addPost(postData: Post, userId: string) {
+ async addPost(postData: Post, userId: string): Promise {
const postsCollectionRef = collection(this.firestore, 'posts');
await addDoc(postsCollectionRef, postData);
@@ -34,64 +33,8 @@ export class FirestoreService {
return collectionData(orderedPostsQuery, { idField: 'id' }) as Observable;
}
- getUsers() {
+ getUsers(): Observable {
const usersRef = collection(this.firestore, 'users');
return collectionData(usersRef, { idField: 'uid' }) as Observable;
}
-
- async followUser(loggedInUserId: string, userToFollow: any): Promise {
- await setDoc(doc(this.firestore, `users/${loggedInUserId}/following`, userToFollow.uid), userToFollow);
-
- const followerData = this.auth.getCurrentUserDetails();
-
- await setDoc(doc(this.firestore, `users/${userToFollow.uid}/followers`, loggedInUserId), followerData);
-
- const userToFollowRef = doc(this.firestore, `users/${userToFollow.uid}`);
- await updateDoc(userToFollowRef, {
- followerCount: increment(1)
- });
-
- const loggedInUserRef = doc(this.firestore, `users/${loggedInUserId}`);
- await updateDoc(loggedInUserRef, {
- followingCount: increment(1)
- });
- }
-
- async unfollowUser(loggedInUserId: string, userToUnfollowId: string): Promise {
- await deleteDoc(doc(this.firestore, `users/${loggedInUserId}/following`, userToUnfollowId));
-
- await deleteDoc(doc(this.firestore, `users/${userToUnfollowId}/followers`, loggedInUserId));
-
- const userToUnfollowRef = doc(this.firestore, `users/${userToUnfollowId}`);
- await updateDoc(userToUnfollowRef, {
- followerCount: increment(-1)
- });
-
- const loggedInUserRef = doc(this.firestore, `users/${loggedInUserId}`);
- await updateDoc(loggedInUserRef, {
- followingCount: increment(-1)
- });
- }
-
- getFollowingUids(loggedInUserId: string): Observable {
- const followingRef = collection(this.firestore, `users/${loggedInUserId}/following`);
- return collectionData(followingRef).pipe(
- map(followingDocs => followingDocs.map(doc => doc['uid']))
- );
- }
-
- getFollowingPosts(userUids: string[]): Observable {
- const postsRef = collection(this.firestore, 'posts');
-
- let postsQuery = query(postsRef, orderBy('timestamp', 'desc'));
- if (userUids.length > 0) {
- postsQuery = query(postsRef, where('uid', 'in', userUids), orderBy('timestamp', 'desc'));
- }
- return collectionData(postsQuery, { idField: 'id' }) as Observable;
- }
-
- getUserById(userId: string): Observable {
- const userRef = doc(this.firestore, `users/${userId}`);
- return docData(userRef);
- }
}
diff --git a/src/app/shared/services/firestore/user-relation.service.spec.ts b/src/app/shared/services/firestore/user-relation.service.spec.ts
new file mode 100644
index 0000000..dfd1ad5
--- /dev/null
+++ b/src/app/shared/services/firestore/user-relation.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { UserRelationService } from './user-relation.service';
+
+describe('UserRelationService', () => {
+ let service: UserRelationService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(UserRelationService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/shared/services/firestore/user-relation.service.ts b/src/app/shared/services/firestore/user-relation.service.ts
new file mode 100644
index 0000000..0c5a0d5
--- /dev/null
+++ b/src/app/shared/services/firestore/user-relation.service.ts
@@ -0,0 +1,98 @@
+import { Injectable, inject } from '@angular/core';
+import { DocumentData, Firestore, collection, collectionData, deleteDoc, doc, docData, increment, query, setDoc, updateDoc, where } from '@angular/fire/firestore';
+import { AuthService } from '../auth/auth.service';
+import { User } from '@angular/fire/auth';
+import { Observable, combineLatest, map, switchMap } from 'rxjs';
+import { Post } from '../../types/Post';
+import { FirestoreService } from './firestore.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UserRelationService {
+
+ private readonly firestore: Firestore = inject(Firestore);
+ private readonly auth: AuthService = inject(AuthService);
+ private readonly firestoreService: FirestoreService = inject(FirestoreService);
+
+
+
+ async followUser(loggedInUserId: string, userToFollow: any): Promise {
+ await setDoc(doc(this.firestore, `users/${loggedInUserId}/following`, userToFollow.uid), userToFollow);
+
+ const followerData = await this.auth.getCurrentUserDetails();
+ console.log(followerData);
+ await setDoc(doc(this.firestore, `users/${userToFollow.uid}/followers`, loggedInUserId), followerData);
+
+ const userToFollowRef = doc(this.firestore, `users/${userToFollow.uid}`);
+ await updateDoc(userToFollowRef, {
+ followerCount: increment(1)
+ });
+
+ const loggedInUserRef = doc(this.firestore, `users/${loggedInUserId}`);
+ await updateDoc(loggedInUserRef, {
+ followingCount: increment(1)
+ });
+ }
+
+ async unfollowUser(loggedInUserId: string, userToUnfollowId: string): Promise {
+ await deleteDoc(doc(this.firestore, `users/${loggedInUserId}/following`, userToUnfollowId));
+
+ await deleteDoc(doc(this.firestore, `users/${userToUnfollowId}/followers`, loggedInUserId));
+
+ const userToUnfollowRef = doc(this.firestore, `users/${userToUnfollowId}`);
+ await updateDoc(userToUnfollowRef, {
+ followerCount: increment(-1)
+ });
+
+ const loggedInUserRef = doc(this.firestore, `users/${loggedInUserId}`);
+ await updateDoc(loggedInUserRef, {
+ followingCount: increment(-1)
+ });
+ }
+
+ getFollowingUids(loggedInUserId: string): Observable {
+ const followingRef = collection(this.firestore, `users/${loggedInUserId}/following`);
+ return collectionData(followingRef).pipe(
+ map(followingDocs => followingDocs.map(doc => doc['uid']))
+ );
+ }
+
+
+
+ getUserById(userId: string): Observable {
+ const userRef = doc(this.firestore, `users/${userId}`);
+ return docData(userRef);
+ }
+
+ getPostsFromFollowing(currentUserId: string): Observable {
+ return this.getFollowingUids(currentUserId).pipe(
+ switchMap(followingUserIds => {
+ const postsRef = collection(this.firestore, 'posts');
+ const postsQuery = query(postsRef, where('uid', 'in', followingUserIds));
+ return collectionData(postsQuery) as Observable;
+ })
+ );
+ }
+
+ getUsersWithFollowingStatus(uid: string) {
+
+ const followingUids$ = this.getFollowingUids(uid);
+ const users$ = this.firestoreService.getUsers();
+
+ return combineLatest([users$, followingUids$]).pipe(
+ map(([users, followingUids]) =>
+ users.map(user => ({
+ ...user,
+ isFollowing: followingUids.includes(user.uid)
+ }))
+ )
+ );
+ }
+
+ getFollowersList(loggedInUserId: string) {
+ const followingRef = collection(this.firestore, `users/${loggedInUserId}/followers`);
+ return collectionData(followingRef, { idField: 'uid' }) as Observable;
+ }
+
+}
diff --git a/src/app/users/users.component.html b/src/app/users/users.component.html
index 3d017b7..5e8c325 100644
--- a/src/app/users/users.component.html
+++ b/src/app/users/users.component.html
@@ -1,36 +1,10 @@
@for (user of usersWithFollowStatus$ | async; track $index) {
- @if(user.uid!==uid) {
-
-
-
-
-
-
-
{{ user.name }}
-
{{ user.email }}
-
Followers: {{ user.followerCount }}
-
-
-
- @if (user.isFollowing) {
-
- } @else{
-
- }
-
-
- } }
+
+ }
diff --git a/src/app/users/users.component.ts b/src/app/users/users.component.ts
index 7938eea..f236f23 100644
--- a/src/app/users/users.component.ts
+++ b/src/app/users/users.component.ts
@@ -2,13 +2,15 @@ import { Auth, onAuthStateChanged } from '@angular/fire/auth';
import { CommonModule } from '@angular/common';
import { Component, inject } from '@angular/core';
import { FirestoreService } from '../shared/services/firestore/firestore.service';
-import { Observable, combineLatest, map, of } from 'rxjs';
+import { Observable, of } from 'rxjs';
import { User } from '../shared/types/User';
+import { UserRelationService } from '../shared/services/firestore/user-relation.service';
+import { OtherUserComponent } from '../shared/components/other-user/other-user.component';
@Component({
selector: 'app-users',
standalone: true,
- imports: [CommonModule],
+ imports: [CommonModule, OtherUserComponent],
templateUrl: './users.component.html',
styleUrl: './users.component.scss'
})
@@ -16,6 +18,8 @@ export class UsersComponent {
private readonly firestore: FirestoreService = inject(FirestoreService);
private readonly auth: Auth = inject(Auth);
+ private readonly userRelations: UserRelationService = inject(UserRelationService);
+
uid: string | undefined = "";
users$: Observable = of([]);
followingUids$: Observable = of([]);
@@ -25,24 +29,15 @@ export class UsersComponent {
ngOnInit() {
this.getUsersList();
this.checkFollowStatus();
- this.uid = this.auth.currentUser?.uid
+ this.uid = this.auth.currentUser?.uid;
}
checkFollowStatus() {
if (this.auth.currentUser) {
- this.followingUids$ = this.firestore.getFollowingUids(this.auth.currentUser.uid);
- this.usersWithFollowStatus$ = combineLatest([this.users$, this.followingUids$]).pipe(
- map(([users, followingUids]) =>
- users.map(user => ({
- ...user,
- isFollowing: followingUids.includes(user.uid)
- }))
- )
- );
+ this.usersWithFollowStatus$ = this.userRelations.getUsersWithFollowingStatus(this.auth.currentUser.uid)
}
}
-
getUsersList() {
this.users$ = this.firestore.getUsers();
}
@@ -52,15 +47,15 @@ export class UsersComponent {
(event.target as HTMLImageElement).style.display = 'none';
}
- followUser(followingUser: string) {
+ followUser(followingUser: User) {
if (this.auth.currentUser) {
- this.firestore.followUser(this.auth.currentUser.uid, followingUser);
+ this.userRelations.followUser(this.auth.currentUser.uid, followingUser);
}
}
unfollowUser(followingUserId: string) {
if (this.auth.currentUser) {
- this.firestore.unfollowUser(this.auth.currentUser.uid, followingUserId);
+ this.userRelations.unfollowUser(this.auth.currentUser.uid, followingUserId);
}
}
}