diff --git a/functions/src/database/groups/index.ts b/functions/src/database/groups/index.ts
new file mode 100644
index 00000000..474e22c8
--- /dev/null
+++ b/functions/src/database/groups/index.ts
@@ -0,0 +1,108 @@
+/***********************************************************************************************
+* Nonprofit Social Networking Platform: Allowing Users and Organizations to Collaborate.
+* Copyright (C) 2023 ASCENDynamics NFP
+*
+* This file is part of Nonprofit Social Networking Platform.
+*
+* Nonprofit Social Networking Platform is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published
+* by the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* Nonprofit Social Networking Platform is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with Nonprofit Social Networking Platform. If not, see .
+***********************************************************************************************/
+import * as functions from "firebase-functions";
+import * as admin from "firebase-admin";
+import * as logger from "firebase-functions/logger";
+
+const db = admin.firestore();
+
+export const onGroupCreation = functions.firestore
+ .document("groups/{groupId}")
+ .onCreate(handleGroupCreation);
+
+/**
+ * Asynchronously handles the creation of groups in the database.
+ * This function logs the group creation, and depending on the status of the new group,
+ * creates a corresponding relationship in the database.
+ * @param {Object} snapshot - The snapshot of the group document that was created.
+ * @param {Object} context - The context in which the function is run, including event context.
+ * @return {Promise} - Returns a promise that resolves when the operation is complete
+ */
+async function handleGroupCreation(snapshot: any, context: any) {
+ logger.info("snapshot: ", snapshot);
+ logger.info("context: ", context);
+ const newGroup = snapshot.data();
+ if (newGroup) {
+ logger.info("newGroup: ", newGroup);
+ // Use a transaction to ensure atomic update
+ return db
+ .runTransaction(async (transaction) => {
+ const userDataSnapshot = await fetchUser(
+ transaction,
+ newGroup.createdBy,
+ );
+ logger.info("userDataSnapshot: ", userDataSnapshot);
+ if (userDataSnapshot.exists) {
+ const userData = userDataSnapshot.data();
+ logger.info("userData: ", userData);
+ const relationshipData = {
+ createdAt: admin.firestore.FieldValue.serverTimestamp(),
+ createdBy: newGroup.createdBy,
+ id: null,
+ senderId: newGroup.createdBy,
+ receiverId: context.params.groupId,
+ type: "member",
+ status: "accepted",
+ membershipRole: "admin",
+ receiverRelationship: "group",
+ senderRelationship: "user",
+ senderName: userData?.displayName, // Assuming the user data has a 'displayName' field
+ senderImage: userData?.profilePicture, // Assuming the user data has an 'profilePicture' field
+ senderTagline: userData?.tagline, // Assuming the user data has a 'tagline' field
+ receiverName: newGroup.name, // Assuming the group data has a 'name' field
+ receiverImage: newGroup.logoImage, // Assuming the group data has an 'logoImage' field
+ receiverTagline: newGroup.tagline, // Assuming the group data has a 'tagline' field
+ lastModifiedAt: admin.firestore.FieldValue.serverTimestamp(),
+ lastModifiedBy: newGroup.createdBy,
+ };
+
+ await transaction.set(
+ db.collection("relationships").doc(),
+ relationshipData,
+ );
+ }
+ })
+ .catch((error) => {
+ logger.error("Transaction failed: ", error);
+ throw error;
+ });
+ }
+}
+
+/**
+ * Fetches a user document based on the provided userId
+ * @param {admin.firestore.Transaction} transaction - The Firestore transaction
+ * @param {string} userId - The ID of the user to fetch
+ * @return {Promise} A promise that resolves with the fetched user document
+ */
+async function fetchUser(
+ transaction: admin.firestore.Transaction,
+ userId: string,
+) {
+ const userRef = db.collection("users").doc(userId);
+ const userDoc = await transaction.get(userRef);
+
+ if (!userDoc.exists) {
+ logger.error("User not found");
+ throw new Error("User not found");
+ }
+
+ return userDoc;
+}
diff --git a/functions/src/database/relationships/index.ts b/functions/src/database/relationships/index.ts
index 21013285..e5ea34b4 100644
--- a/functions/src/database/relationships/index.ts
+++ b/functions/src/database/relationships/index.ts
@@ -1,3 +1,22 @@
+/***********************************************************************************************
+* Nonprofit Social Networking Platform: Allowing Users and Organizations to Collaborate.
+* Copyright (C) 2023 ASCENDynamics NFP
+*
+* This file is part of Nonprofit Social Networking Platform.
+*
+* Nonprofit Social Networking Platform is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published
+* by the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* Nonprofit Social Networking Platform is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with Nonprofit Social Networking Platform. If not, see .
+***********************************************************************************************/
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import * as logger from "firebase-functions/logger";
diff --git a/functions/src/index.ts b/functions/src/index.ts
index 1e6e56e7..1b59bc19 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -1,3 +1,23 @@
+/***********************************************************************************************
+* Nonprofit Social Networking Platform: Allowing Users and Organizations to Collaborate.
+* Copyright (C) 2023 ASCENDynamics NFP
+*
+* This file is part of Nonprofit Social Networking Platform.
+*
+* Nonprofit Social Networking Platform is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published
+* by the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* Nonprofit Social Networking Platform is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with Nonprofit Social Networking Platform. If not, see .
+***********************************************************************************************/
+
/**
* Import function triggers from their respective submodules:
*
@@ -17,10 +37,5 @@
// logger.info("Hello logs!", {structuredData: true});
// response.send("Hello from Firebase!");
// });
-import {
- onRelationshipCreation,
- onRelationshipDeletion,
- onRelationshipUpdate,
-} from "./database/relationships"; // relationships triggers
-
-export {onRelationshipCreation, onRelationshipDeletion, onRelationshipUpdate}; // relationships triggers
+export * from "./database/relationships"; // triggers
+export * from "./database/groups"; // triggers
diff --git a/src/app/core/services/groups.service.ts b/src/app/core/services/groups.service.ts
index ce02e3c1..77239f75 100644
--- a/src/app/core/services/groups.service.ts
+++ b/src/app/core/services/groups.service.ts
@@ -58,12 +58,19 @@ export class GroupsService {
private successHandler: SuccessHandlerService,
) {}
- async createGroup(group: AppGroup): Promise {
+ async createGroup(group: Partial): Promise {
const loading = await this.loadingController.create();
await loading.present();
+ const userId = this.authService.getCurrentUser()?.uid;
+ if (userId) {
+ group.admins = [userId];
+ group.members = [userId];
+ }
+ group.logoImage = "assets/icon/favicon.png";
+ group.heroImage = "assets/image/orghero.png";
return await addDoc(
collection(this.firestoreService.firestore, this.collectionName),
- prepareDataForCreate(group, this.authService.getCurrentUser()?.uid),
+ prepareDataForCreate(group, userId),
)
.then((docRef) => {
this.successHandler.handleSuccess("Request sent successfully!");
diff --git a/src/app/models/user.model.ts b/src/app/models/user.model.ts
index 3ec1380f..e072cebc 100644
--- a/src/app/models/user.model.ts
+++ b/src/app/models/user.model.ts
@@ -40,6 +40,7 @@ export interface AppUser {
email: string; // Email address
emailVerified: boolean; // Whether the user's email is verified
name: string; // First and last name
+ heroImage: string; // base64 string
profilePicture: string; // base64 string
dateOfBirth: Timestamp; // Birthday
language: string; // User's language
diff --git a/src/app/modules/group/components/create-group-modal/create-group-modal.component.html b/src/app/modules/group/components/create-group-modal/create-group-modal.component.html
new file mode 100644
index 00000000..22fc7145
--- /dev/null
+++ b/src/app/modules/group/components/create-group-modal/create-group-modal.component.html
@@ -0,0 +1,56 @@
+
+
+
+ Create New Group
+
+ Close
+
+
+
+
+
+
+
diff --git a/src/app/modules/group/components/create-group-modal/create-group-modal.component.scss b/src/app/modules/group/components/create-group-modal/create-group-modal.component.scss
new file mode 100644
index 00000000..641816b5
--- /dev/null
+++ b/src/app/modules/group/components/create-group-modal/create-group-modal.component.scss
@@ -0,0 +1,19 @@
+/***********************************************************************************************
+* Nonprofit Social Networking Platform: Allowing Users and Organizations to Collaborate.
+* Copyright (C) 2023 ASCENDynamics NFP
+*
+* This file is part of Nonprofit Social Networking Platform.
+*
+* Nonprofit Social Networking Platform is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published
+* by the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* Nonprofit Social Networking Platform is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with Nonprofit Social Networking Platform. If not, see .
+***********************************************************************************************/
diff --git a/src/app/modules/group/components/create-group-modal/create-group-modal.component.spec.ts b/src/app/modules/group/components/create-group-modal/create-group-modal.component.spec.ts
new file mode 100644
index 00000000..e55f1df5
--- /dev/null
+++ b/src/app/modules/group/components/create-group-modal/create-group-modal.component.spec.ts
@@ -0,0 +1,42 @@
+/***********************************************************************************************
+* Nonprofit Social Networking Platform: Allowing Users and Organizations to Collaborate.
+* Copyright (C) 2023 ASCENDynamics NFP
+*
+* This file is part of Nonprofit Social Networking Platform.
+*
+* Nonprofit Social Networking Platform is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published
+* by the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* Nonprofit Social Networking Platform is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with Nonprofit Social Networking Platform. If not, see .
+***********************************************************************************************/
+import {ComponentFixture, TestBed, waitForAsync} from "@angular/core/testing";
+import {IonicModule} from "@ionic/angular";
+
+import {CreateGroupModalComponent} from "./create-group-modal.component";
+
+describe("CreateGroupModalComponent", () => {
+ let component: CreateGroupModalComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [IonicModule.forRoot()],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(CreateGroupModalComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ }));
+
+ it("should create", () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/modules/group/components/create-group-modal/create-group-modal.component.ts b/src/app/modules/group/components/create-group-modal/create-group-modal.component.ts
new file mode 100644
index 00000000..9f070de0
--- /dev/null
+++ b/src/app/modules/group/components/create-group-modal/create-group-modal.component.ts
@@ -0,0 +1,67 @@
+/***********************************************************************************************
+* Nonprofit Social Networking Platform: Allowing Users and Organizations to Collaborate.
+* Copyright (C) 2023 ASCENDynamics NFP
+*
+* This file is part of Nonprofit Social Networking Platform.
+*
+* Nonprofit Social Networking Platform is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as published
+* by the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* Nonprofit Social Networking Platform is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with Nonprofit Social Networking Platform. If not, see .
+***********************************************************************************************/
+import {Component} from "@angular/core";
+import {IonicModule, ModalController} from "@ionic/angular";
+import {CommonModule} from "@angular/common";
+import {
+ FormBuilder,
+ FormsModule,
+ ReactiveFormsModule,
+ Validators,
+} from "@angular/forms";
+import {GroupsService} from "../../../../core/services/groups.service";
+import {AppGroup} from "../../../../models/group.model";
+
+@Component({
+ selector: "app-create-group-modal",
+ templateUrl: "./create-group-modal.component.html",
+ styleUrls: ["./create-group-modal.component.scss"],
+ standalone: true,
+ imports: [CommonModule, IonicModule, FormsModule, ReactiveFormsModule],
+})
+export class CreateGroupModalComponent {
+ groupForm = this.fb.group({
+ name: ["", Validators.required],
+ description: ["", Validators.required],
+ tagline: ["", Validators.required],
+ });
+
+ constructor(
+ private modalCtrl: ModalController,
+ private fb: FormBuilder,
+ private groupsService: GroupsService,
+ ) {}
+
+ cancel() {
+ return this.modalCtrl.dismiss(null, "cancel");
+ }
+
+ confirm() {
+ return this.modalCtrl.dismiss(null, "confirm");
+ }
+
+ onSubmit() {
+ this.groupsService
+ .createGroup(this.groupForm.value as Partial)
+ .then((groupId) => {
+ return this.modalCtrl.dismiss({groupId: groupId}, "confirm");
+ });
+ }
+}
diff --git a/src/app/modules/group/pages/group-list/group-list.page.html b/src/app/modules/group/pages/group-list/group-list.page.html
index b991bb06..5bbbbeaf 100644
--- a/src/app/modules/group/pages/group-list/group-list.page.html
+++ b/src/app/modules/group/pages/group-list/group-list.page.html
@@ -51,9 +51,9 @@
>
diff --git a/src/app/modules/group/pages/group-profile/components/hero/hero.component.html b/src/app/modules/group/pages/group-profile/components/hero/hero.component.html
index 23964de3..8de2b158 100644
--- a/src/app/modules/group/pages/group-profile/components/hero/hero.component.html
+++ b/src/app/modules/group/pages/group-profile/components/hero/hero.component.html
@@ -29,6 +29,9 @@
{{ group.tagline }}
### Hours Volunteer
+
diff --git a/src/app/modules/group/pages/group-profile/components/hero/hero.component.ts b/src/app/modules/group/pages/group-profile/components/hero/hero.component.ts
index 408f7be6..33f824a7 100644
--- a/src/app/modules/group/pages/group-profile/components/hero/hero.component.ts
+++ b/src/app/modules/group/pages/group-profile/components/hero/hero.component.ts
@@ -31,7 +31,8 @@ import {AppGroup} from "../../../../../../models/group.model";
})
export class HeroComponent implements OnInit {
@Input() group: Partial | null = null; // define your user here
-
+ @Input() isMember: boolean = false;
+ @Input() isPendingMember: boolean = false;
constructor() {}
ngOnInit() {}
diff --git a/src/app/modules/group/pages/group-profile/group-profile.page.html b/src/app/modules/group/pages/group-profile/group-profile.page.html
index 87988738..889e12ae 100644
--- a/src/app/modules/group/pages/group-profile/group-profile.page.html
+++ b/src/app/modules/group/pages/group-profile/group-profile.page.html
@@ -35,7 +35,12 @@
-
+
@@ -45,7 +50,7 @@
diff --git a/src/app/modules/group/pages/group-profile/group-profile.page.ts b/src/app/modules/group/pages/group-profile/group-profile.page.ts
index e84b6399..0572e75b 100644
--- a/src/app/modules/group/pages/group-profile/group-profile.page.ts
+++ b/src/app/modules/group/pages/group-profile/group-profile.page.ts
@@ -49,23 +49,23 @@ import {GroupListComponent} from "./components/group-list/group-list.component";
],
})
export class GroupProfilePage implements OnInit {
- groupId: string | null;
+ groupId: string | null = "";
group: Partial | null = {};
- user: any;
memberList: AppRelationship[] = [];
groupList: AppRelationship[] = [];
- canEdit: boolean = false;
+ isAdmin: boolean = false;
+ isMember: boolean = false;
+ isPendingMember: boolean = false;
constructor(
private authService: AuthService,
private route: ActivatedRoute,
private menuService: MenuService,
private groupsService: GroupsService,
private relationshipsCollectionService: RelationshipsCollectionService,
- ) {
- this.groupId = this.route.snapshot.paramMap.get("groupId");
- }
+ ) {}
ngOnInit() {
+ this.groupId = this.route.snapshot.paramMap.get("groupId");
this.getGroup();
this.relationshipsCollectionService
.getRelationships(this.groupId)
@@ -97,10 +97,17 @@ export class GroupProfilePage implements OnInit {
.getGroupById(this.groupId)
.then((group) => {
this.group = group;
- let userId = this.authService?.getCurrentUser()?.uid;
- this.canEdit = userId
+ let user = this.authService.getCurrentUser();
+ let userId = user?.uid ? user.uid : "";
+ this.isAdmin = userId
? this.group?.admins?.includes(userId) || false
: false;
+ this.isMember = userId
+ ? this.group?.members?.includes(userId) || false
+ : false;
+ this.isPendingMember = userId
+ ? this.group?.pendingMembers?.includes(userId) || false
+ : false;
})
.catch((error) => {
console.log(error);
diff --git a/src/app/modules/user/pages/user-profile/components/hero/hero.component.html b/src/app/modules/user/pages/user-profile/components/hero/hero.component.html
index 0331f8d8..00d9a2db 100644
--- a/src/app/modules/user/pages/user-profile/components/hero/hero.component.html
+++ b/src/app/modules/user/pages/user-profile/components/hero/hero.component.html
@@ -18,11 +18,7 @@
along with Nonprofit Social Networking Platform. If not, see .
-->
-
+
{{ user.displayName }}
101 Hours Volunteer
diff --git a/src/app/modules/user/pages/user-profile/components/hero/hero.component.ts b/src/app/modules/user/pages/user-profile/components/hero/hero.component.ts
index ba60d5a3..d652175b 100644
--- a/src/app/modules/user/pages/user-profile/components/hero/hero.component.ts
+++ b/src/app/modules/user/pages/user-profile/components/hero/hero.component.ts
@@ -20,7 +20,7 @@
import {CommonModule} from "@angular/common";
import {Component, Input, OnInit} from "@angular/core";
import {IonicModule} from "@ionic/angular";
-import {User} from "firebase/auth";
+import {AppUser} from "../../../../../../models/user.model"; // import your user model
@Component({
selector: "app-hero",
@@ -30,7 +30,7 @@ import {User} from "firebase/auth";
imports: [IonicModule, CommonModule],
})
export class HeroComponent implements OnInit {
- @Input() user: User | null = null; // define your user here
+ @Input() user: AppUser | null = null; // define your user here
constructor() {}
diff --git a/src/app/shared/components/menu/menu.component.html b/src/app/shared/components/menu/menu.component.html
index 2554dc3f..4a3b1a67 100644
--- a/src/app/shared/components/menu/menu.component.html
+++ b/src/app/shared/components/menu/menu.component.html
@@ -30,13 +30,13 @@
-
+
-
+
+ User
+
{{ p.title }}
+ {{ p.buttonText }}
+
+
@@ -87,11 +112,11 @@
-
+
-
+
+ User
+
{{ p.title }}
+
+
{{ p.title }}
- -->
+
+
+ ASCENDynamics NFP Information
+
+
+
+ {{ p.title }}
+
+