diff --git a/src/app/components/about/about.component.html b/src/app/components/about/about.component.html index 5bf352e..6f2cb3e 100644 --- a/src/app/components/about/about.component.html +++ b/src/app/components/about/about.component.html @@ -20,7 +20,7 @@ - + diff --git a/src/app/components/about/about.component.ts b/src/app/components/about/about.component.ts index 3983dc3..2fdf072 100644 --- a/src/app/components/about/about.component.ts +++ b/src/app/components/about/about.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit, ViewChild} from '@angular/core'; import {Activitylist} from 'src/app/models/activity'; -import {Levellist} from 'src/app/models/levellist'; +import {LevelList} from 'src/app/models/levellist'; import {MatSort} from '@angular/material/sort'; import {MatTableDataSource} from '@angular/material/table'; import {ActivityService} from 'src/app/services/activity/activity.service'; @@ -12,12 +12,12 @@ import {ActivityService} from 'src/app/services/activity/activity.service'; }) export class AboutComponent implements OnInit { actionlist: Activitylist = new Activitylist(); - levellist: Levellist = new Levellist(); + levellist: LevelList = new LevelList(); displayedColumns = ['points', 'name', 'description']; dataSource = new MatTableDataSource(this.actionlist.Actions); displayedLevelColumns = ['level', 'name']; - levelSource = this.levellist.levels; + levelSource = new MatTableDataSource(this.levellist.levels); constructor(private activityService: ActivityService) { } @@ -31,6 +31,12 @@ export class AboutComponent implements OnInit { this.dataSource = new MatTableDataSource(this.actionlist.Actions); this.dataSource.sort = this.sort; }); + this.activityService.getLevels(); + this.activityService.levelList.subscribe(response => { + this.levellist = response; + this.levelSource = new MatTableDataSource(this.levellist.levels); + this.levelSource.sort = this.sort; + }); } } diff --git a/src/app/components/feed/feed.component.html b/src/app/components/feed/feed.component.html index 00f4bbb..b3c31e9 100644 --- a/src/app/components/feed/feed.component.html +++ b/src/app/components/feed/feed.component.html @@ -22,8 +22,9 @@ - + {{content.value.length}} / 2048
diff --git a/src/app/components/feed/feed.component.ts b/src/app/components/feed/feed.component.ts index abb2992..97cbdb5 100644 --- a/src/app/components/feed/feed.component.ts +++ b/src/app/components/feed/feed.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit, ViewChild, ElementRef} from '@angular/core'; import {Post} from 'src/app/models/post'; -import {FeedService, Sort} from 'src/app/services/feed/feed.service'; +import {FeedService, Sort, PostingState} from 'src/app/services/feed/feed.service'; import {Activitylist} from 'src/app/models/activity'; import {DatasharingService} from '../../services/datasharing.service'; import {ActivityService} from 'src/app/services/activity/activity.service'; @@ -63,10 +63,14 @@ export class FeedComponent implements OnInit { this.feedService.postsAvailable.subscribe(available => { this.loadingMostLiked = this.loadingNew = available; }); - this.feedService.posting.subscribe(posting => { + this.feedService.postingState.subscribe(postingState => { const temp = this.posting; - this.posting = posting; - if (temp !== this.posting && !this.posting) { + + this.posting = postingState.posting; + this.errorOccurred = postingState.errorOccured; + this.errorMessage = postingState.errorMessage; + + if (!this.posting && this.posting !== temp && !postingState.errorOccured) { this.resetPostInput(); } }); @@ -79,22 +83,9 @@ export class FeedComponent implements OnInit { */ createPost(postElement, activityId: string) { if (postElement && activityId && this.checked) { - this.posting = true; - this.feedService.createPostActivity(postElement.value, activityId, this.file).subscribe(() => { - }, (error: IErrorResponse) => { - this.errorOccurred = true; - this.posting = false; - this.errorMessage = error.error.errors[0].message; - }); + this.feedService.createPostActivity(postElement.value, activityId, this.file).subscribe(); } else if (postElement) { - this.posting = true; - this.feedService.createPost(postElement.value, this.file).subscribe((result) => { - }, (error: IErrorResponse) => { - console.log(error); - this.posting = false; - this.errorOccurred = true; - this.errorMessage = error.error.errors[0].message; - }); + this.feedService.createPost(postElement.value, this.file).subscribe(); } } diff --git a/src/app/components/feed/postlist/postlist.component.html b/src/app/components/feed/postlist/postlist.component.html index f07a137..f74d789 100644 --- a/src/app/components/feed/postlist/postlist.component.html +++ b/src/app/components/feed/postlist/postlist.component.html @@ -1,16 +1,23 @@
- +
- - + + + +
@@ -22,10 +29,11 @@
- +
- post image + post image
{{post.upvotes}}
{{post.downvotes}}
@@ -57,4 +69,4 @@
- + \ No newline at end of file diff --git a/src/app/components/feed/postlist/postlist.component.ts b/src/app/components/feed/postlist/postlist.component.ts index 84e2044..fee24fa 100644 --- a/src/app/components/feed/postlist/postlist.component.ts +++ b/src/app/components/feed/postlist/postlist.component.ts @@ -1,7 +1,10 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; -import {Post} from 'src/app/models/post'; -import {FeedService} from 'src/app/services/feed/feed.service'; -import {Router} from '@angular/router'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Post } from 'src/app/models/post'; +import { FeedService } from 'src/app/services/feed/feed.service'; +import { Router } from '@angular/router'; +import { DatasharingService } from 'src/app/services/datasharing.service'; +import { ActivityService } from 'src/app/services/activity/activity.service'; +import { ReportReason } from 'src/app/models/reportReason'; @Component({ selector: 'feed-postlist', @@ -13,11 +16,23 @@ export class PostlistComponent implements OnInit { @Input() childPostList: Post[]; @Output() voteEvent = new EventEmitter(); selectedPost: Post; + loggedIn = false; + reportReasons: ReportReason[] = new Array(); - constructor(private feedService: FeedService, private router: Router) { + constructor(private feedService: FeedService, + private data: DatasharingService, + private router: Router, + private activityService: ActivityService) { } ngOnInit() { + this.data.currentUser.subscribe(user => { + this.loggedIn = user.loggedIn; + }); + this.activityService.getReportReasons(); + this.activityService.reportReasonList.subscribe(response => { + this.reportReasons = response; + }); } voteUp(pPost: Post) { @@ -49,6 +64,10 @@ export class PostlistComponent implements OnInit { }); } + reportPost(reason: ReportReason, post: Post) { + this.feedService.reportPost(reason.id, post.id).subscribe(); + } + onLoad(post: Post) { post.mediaLoading = false; } diff --git a/src/app/components/group/group.component.html b/src/app/components/group/group.component.html index 652f47b..5b80133 100644 --- a/src/app/components/group/group.component.html +++ b/src/app/components/group/group.component.html @@ -4,40 +4,30 @@
- + camera_alt
- +
{{groupProfile.name}}
- - -
-
@@ -45,8 +35,8 @@
- created by {{groupProfile.creator.username}} + created by + {{groupProfile.creator.username}} @{{groupProfile.creator.handle}}
@@ -60,42 +50,32 @@
- + camera_alt
- +
{{groupProfile.name}} - created by {{groupProfile.creator.username}} + created by + {{groupProfile.creator.username}} @{{groupProfile.creator.handle}}
- - -
-
@@ -116,24 +96,24 @@
- + {{event.name}} {{event.date}}
- - +
@@ -145,8 +125,32 @@ Members -
- +
+ + +
+ +
+ {{user.username}} + {{user.handle}} + +
+ + + + + +
+
+
@@ -156,5 +160,4 @@

Group not found :(

-
- +
\ No newline at end of file diff --git a/src/app/components/group/group.component.sass b/src/app/components/group/group.component.sass index 6046785..8125bfb 100644 --- a/src/app/components/group/group.component.sass +++ b/src/app/components/group/group.component.sass @@ -107,13 +107,63 @@ $mat-card-header-size: 100px !default margin-top: 0.5em margin-bottom: 0.5em - .profile-picture - background-image: url(https://material.angular.io/assets/img/examples/shiba1.jpg) - background-size: cover - .profile-picture:hover cursor: pointer +$mat-card-header-size-small: 54px !default +.profile-picture-small + height: $mat-card-header-size-small + width: $mat-card-header-size-small + border-radius: 50% + flex-shrink: 0 + background-size: cover + transition-duration: 0.5s + z-index: 10 + object-fit: cover + +.member-card + box-sizing: border-box + width: 100% + margin-top: 0.5em + outline: none + user-select: none + + ::ng-deep .mat-card-header-text + width: 1000% + margin: auto 0 auto 24px + + .mat-card-subtitle + margin: 0 + word-break: break-all + + .mat-card-title + margin: 0 + word-break: break-all + + .request-button + margin-top: 0.5em + margin-bottom: 0.5em + + .pointer:hover + cursor: pointer + + .icon-box + text-align: right + width: 100% + + $pic-size: 40px !default + .card-picture + cursor: pointer + height: $pic-size + width: $pic-size + border-radius: 50% + flex-shrink: 0 + background-size: cover + transition-duration: 0.5s + z-index: 10 + object-fit: cover + + .pointer cursor: pointer diff --git a/src/app/components/group/group.component.ts b/src/app/components/group/group.component.ts index 014483b..e639a62 100644 --- a/src/app/components/group/group.component.ts +++ b/src/app/components/group/group.component.ts @@ -1,15 +1,15 @@ -import {Component, OnInit, ViewChild} from '@angular/core'; -import {Data, NavigationEnd, Router} from '@angular/router'; -import {User} from 'src/app/models/user'; -import {MatSort} from '@angular/material/sort'; -import {RequestService} from 'src/app/services/request/request.service'; -import {DatasharingService} from '../../services/datasharing.service'; -import {GroupService} from 'src/app/services/group/group.service'; -import {Group} from 'src/app/models/group'; -import {Event} from 'src/app/models/event'; -import {MatDialog, MatDialogRef} from '@angular/material/dialog'; -import {DialogGroupFileUploadComponent} from './fileUpload/fileUpload.component'; -import {Lightbox} from 'ngx-lightbox'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { Data, NavigationEnd, Router } from '@angular/router'; +import { User } from 'src/app/models/user'; +import { MatSort } from '@angular/material/sort'; +import { RequestService } from 'src/app/services/request/request.service'; +import { DatasharingService } from '../../services/datasharing.service'; +import { GroupService } from 'src/app/services/group/group.service'; +import { Group } from 'src/app/models/group'; +import { Event } from 'src/app/models/event'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { DialogGroupFileUploadComponent } from './fileUpload/fileUpload.component'; +import { Lightbox } from 'ngx-lightbox'; // DIALOG COMPONENT to create events @Component({ @@ -76,6 +76,7 @@ export class GroupComponent implements OnInit { self: User; id: string; isAdmin = false; + isCreator = false; groupNotFound = false; loading = false; @@ -100,7 +101,7 @@ export class GroupComponent implements OnInit { }); } - @ViewChild(MatSort, {static: true}) sort: MatSort; + @ViewChild(MatSort, { static: true }) sort: MatSort; ngOnInit() { this.loading = true; @@ -120,6 +121,11 @@ export class GroupComponent implements OnInit { this.isAdmin = true; } } + if (this.groupProfile.creator.userID === this.self.userID) { + this.isCreator = true; + } else { + this.isCreator = false; + } for (const member of this.groupProfile.members) { member.allowedToSendRequest = this.requestService.isAllowedToSendRequest(member.userID, this.self); } @@ -174,6 +180,10 @@ export class GroupComponent implements OnInit { }); } + public deleteEvent(event: Event) { + this.groupService.deleteEvent(event.id).subscribe(); + } + public showUserProfile(user: User) { this.router.navigate(['profile/' + user.userID]); } @@ -185,9 +195,9 @@ export class GroupComponent implements OnInit { private deleteGroup() { this.groupService.deleteGroup(this.groupProfile.id) - .subscribe(response => { - this.router.navigateByUrl(''); - }); + .subscribe(response => { + this.router.navigateByUrl(''); + }); } leaveGroup() { @@ -198,11 +208,23 @@ export class GroupComponent implements OnInit { }); } + addGroupAdmin(user: User) { + this.groupService.addGroupAdmin(user.userID.toString(), this.id).subscribe(response => { + user.isGroupAdmin = true; + }); + } + + removeGroupAdmin(user: User) { + this.groupService.removeGroupAdmin(user.userID.toString(), this.id).subscribe(response => { + user.isGroupAdmin = false; + }); + } + openPfpLightbox() { this.lightbox.open([{ src: this.groupProfile.picture, thumb: this.groupProfile.picture, - }], 0, {disableScrolling: true}); + }], 0, { disableScrolling: true }); } } diff --git a/src/app/components/imprint/imprint.component.html b/src/app/components/imprint/imprint.component.html index d53a447..718b468 100644 --- a/src/app/components/imprint/imprint.component.html +++ b/src/app/components/imprint/imprint.component.html @@ -1,6 +1,147 @@ -Imprint +
-

The greenvironment network is being developed by the greenvironment team

-

Contact

-

Email: nick.derkoenig@greenvironment.net

+Imprint +
+

Privacy Policy

+

Last updated: January 22, 2020

+

This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information + when You use the Service and tells You about Your privacy rights and how the law protects You.

+

Interpretation and Definitions

+

Interpretation

+

The words of which the initial letter is capitalized have meanings defined under the following conditions.

+

The following definitions shall have the same meaning regardless of whether they appear in singular or in plural. +

+

Definitions

+

For the purposes of this Privacy Policy:

+
    +
  • +

    You means the individual accessing or using the Service, or We, or other legal entity on + behalf of which such individual is accessing or using the Service, as applicable.

    +
  • +
  • +

    Team Greenvironment (referred to as either "Team Greenvironment", "We", + "Us" or "Our" in this Agreement) refers to the Greenvironment team.

    +
  • +
  • +

    Account means a unique account created for You to access our Service or parts of our Service. +

    +
  • +
  • +

    Website refers to Greenvironment, accessible from https://greenvironment.net/

    +
  • +
  • +

    Service refers to the Website.

    +
  • +
  • +

    Third-party Social Media Service refers to any website or any social network website through + which a User can log in or create an account to use the Service.

    +
  • +
  • +

    Personal Data is any information that relates to an identified or identifiable individual.

    +
  • +
  • +

    Cookies are small files that are placed on Your computer, mobile device or any other device by + a website, containing the details of Your browsing history on that website among its many uses.

    +
  • +
  • +

    Usage Data refers to data collected automatically, either generated by the use of the Service + or from the Service infrastructure itself (for example, the duration of a page visit).

    +
  • +
+

Collecting and Using Your Personal Data

+

Types of Data Collected

+

Account Data

+

While using Our Service, We may ask You to provide Us with certain personally identifiable information that will be + saved to your account:

+

Email Address

+

To identify a unique user as a human and to provide a way to log in.

+

Password

+

To provide a secured access to your account. It will only be saved as a hashed value.

+

Username

+

The display name of the user as a human friendly way of representation.

+

Handle

+

A unique username that can be used for identification.

+

Posts

+

Collected to display them to other users.

+

Votes

+

The Upvote or Downvote performed on a post to display the sum of votes all users performed.

+

Profile Pictures

+

A way to enhance the users profile. Stored public accessible on the server.

+

Invites

+

Only visible to the user who was addressed.

+

Friends

+

Only the User and his friend can see that they are friends.The data will be deleted after the friendship is + canceled.

+

Rang points and rang

+

Collected to show other user how good the user helped to protect the environment. Rang and rang points are visible + to all users.

+

Groups/Events

+

The date is used to assign the user to groups and events. Collected to show them to other group- /Event- members. +

+

Chatroom

+

The member are showed to each others.

+

Chat Messages

+

Stored in plain text in the database. The message is visible to all other group members even if the user left the + group

+

Cookies

+

We use Cookies to save the session of the user. So if the user visits Greenvironment the next time he is already + logged in. You can instruct your browser to refuse all Cookies or to indicate when a Cookie is being saved. However, + if ou do not accept Cookies, You may not be able to use some parts of our Service.

+

Use of Your Personal Data

+

Your Personal Data will be used for the following purposes:

+
    +
  • To manage Your Account: to manage Your registration as a user of the Service. The Personal Data + You provide can give You access to different functionalities of the Service that are available to You as a + registered user.
  • +
  • To contact You: To contact You by email.
  • +
+

Retention of Your Personal Data

+

We will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. + We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, + if we are required to retain your data to comply with applicable laws), resolve disputes, and enforce our legal + agreements and policies.

+

Transfer of Your Personal Data

+

Your information, including Personal Data, is processed at Our operating offices and in any other places where the + parties involved in the processing are located. It means that this information may be transferred to — and + maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where + the data protection laws may differ than those from Your jurisdiction.

+

Your consent to this Privacy Policy followed by Your submission of such information represents Your agreement to + that transfer.

+

We will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with + this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless + there are adequate controls in place including the security of Your data and other personal information.

+

Disclosure of Your Personal Data

+ +

We may disclose Your Personal Data in the good faith belief that such action is necessary to:

+
    +
  • Comply with a legal obligation
  • +
  • Protect and defend the rights or property of the Greenvironment Team
  • +
  • Prevent or investigate possible wrongdoing in connection with the Service
  • +
  • Protect the personal safety of Users of the Service or the public
  • +
  • Protect against legal liability
  • +
+

Security of Your Personal Data

+

The security of Your Personal Data is important to Us, but remember that no method of transmission over the + Internet, or method of electronic storage is 100% secure. While We strive to use technical acceptable means to + protect Your Personal Data, We cannot guarantee its absolute security.

+

Links to Other Websites

+

Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, + You will be directed to that third party's site. We strongly advise You to review the Privacy Policy of every site + You visit.

+

We have no control over and assume no responsibility for the content, privacy policies or practices of any third + party sites or services.

+

Changes to this Privacy Policy

+

We may update our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy + Policy on this page.

+

We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and + update the "Last updated" date at the top of this Privacy Policy.

+

You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are + effective when they are posted on this page.

+

Contact Us

+

If you have any questions about this Privacy Policy, You can contact us:

+ +
\ No newline at end of file diff --git a/src/app/components/imprint/imprint.component.sass b/src/app/components/imprint/imprint.component.sass index 571666b..81ba057 100644 --- a/src/app/components/imprint/imprint.component.sass +++ b/src/app/components/imprint/imprint.component.sass @@ -3,12 +3,18 @@ #imprint - padding: 2em - max-width: 35em + position: fixed + width: 100% + height: calc(100% - 56px) + overflow: scroll + +#text-box margin: 0 auto + max-width: 800px + padding: 2em + ::ng-deep a + color: $primary-color -h1.mat-display-1 - margin: 0 diff --git a/src/app/components/main-navigation/main-navigation.component.ts b/src/app/components/main-navigation/main-navigation.component.ts index a9bcdc2..3898ce2 100644 --- a/src/app/components/main-navigation/main-navigation.component.ts +++ b/src/app/components/main-navigation/main-navigation.component.ts @@ -4,7 +4,6 @@ import {DatasharingService} from '../../services/datasharing.service'; import {RequestService} from '../../services/request/request.service'; import {SettingsService} from '../../services/settings/settings.service'; import {environment} from 'src/environments/environment'; -import {Levellist} from 'src/app/models/levellist'; import {Router} from '@angular/router'; import {User} from 'src/app/models/user'; import {OverlayContainer} from '@angular/cdk/overlay'; @@ -33,7 +32,6 @@ export class MainNavigationComponent implements OnInit { userId: number; username: string; user: User; - levellist: Levellist = new Levellist(); level: string; points: number; profileUrl = '/profile/1'; diff --git a/src/app/components/profile/profile.component.ts b/src/app/components/profile/profile.component.ts index 200f2d9..22232a7 100644 --- a/src/app/components/profile/profile.component.ts +++ b/src/app/components/profile/profile.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {NavigationEnd, Router} from '@angular/router'; import {User} from 'src/app/models/user'; -import {Levellist} from 'src/app/models/levellist'; +import {LevelList} from 'src/app/models/levellist'; import {RequestService} from 'src/app/services/request/request.service'; import {DatasharingService} from '../../services/datasharing.service'; import {ProfileService} from 'src/app/services/profile/profile.service'; @@ -18,7 +18,7 @@ import {Lightbox} from 'ngx-lightbox'; styleUrls: ['./profile.component.sass'] }) export class ProfileComponent implements OnInit { - levellist: Levellist = new Levellist(); + levellist: LevelList = new LevelList(); ownProfile = false; userProfile: User = new User(); self: User; diff --git a/src/app/components/register/register.component.html b/src/app/components/register/register.component.html index fb5b370..635da5f 100644 --- a/src/app/components/register/register.component.html +++ b/src/app/components/register/register.component.html @@ -28,6 +28,14 @@
+

+ I am older than 16 years. + +

+

+ I've read the privacy policy. + +

diff --git a/src/app/components/register/register.component.sass b/src/app/components/register/register.component.sass index 96133b0..f80b13a 100644 --- a/src/app/components/register/register.component.sass +++ b/src/app/components/register/register.component.sass @@ -5,6 +5,8 @@ padding: 2em max-width: 35em margin: 0 auto + ::ng-deep a + color: $primary-color .example-container display: flex @@ -26,6 +28,10 @@ input.example-right-align .mat-error margin-bottom: 0.25em +.check-box + float: left + margin-right: 1em + .mat-raised-button margin: 0.25em width: 100% diff --git a/src/app/components/register/register.component.ts b/src/app/components/register/register.component.ts index abd544e..ca5f1b9 100644 --- a/src/app/components/register/register.component.ts +++ b/src/app/components/register/register.component.ts @@ -14,6 +14,8 @@ export class RegisterComponent implements OnInit { errorMessage: string; hide1 = true; hide2 = true; + ageCheck = false; + imprintCheck = false; constructor(private registerService: RegisterService) { this.registration = {username: null, passwordHash: null, email: null}; @@ -28,7 +30,7 @@ export class RegisterComponent implements OnInit { onClickSubmit(pUsername: string, pEmail: string, pPasswordHash: string, pPasswordHashRepeat: string) { this.errorOccurred = false; this.errorMessage = ' '; - if (this.passwordSame(pPasswordHash, pPasswordHashRepeat)) { + if (this.passwordSame(pPasswordHash, pPasswordHashRepeat) && this.boxesChecked()) { this.registration.username = pUsername.trim(); this.registration.email = pEmail.trim().toLowerCase(); this.registration.passwordHash = sha512.sha512(pPasswordHash); @@ -38,7 +40,6 @@ export class RegisterComponent implements OnInit { passwordSame(pwd: string, pwd2: string) { if (pwd === pwd2) { - console.log('password same'); return true; } else { this.errorOccurred = true; @@ -47,6 +48,21 @@ export class RegisterComponent implements OnInit { } } + boxesChecked(): boolean { + if (this.imprintCheck && this.ageCheck) { + console.log('all boxes checked'); + return true; + } else { + this.errorOccurred = true; + if (!this.ageCheck) { + this.errorMessage = 'You have to confirm your age.'; + } else if (!this.imprintCheck) { + this.errorMessage = 'You have to confirm that you read the privacy policy.'; + } + return false; + } + } + ngOnInit() { } diff --git a/src/app/models/event.ts b/src/app/models/event.ts index a01d2f7..e65111e 100644 --- a/src/app/models/event.ts +++ b/src/app/models/event.ts @@ -1,10 +1,11 @@ -import {IEvent} from './interfaces/IEvent'; +import { IEvent } from './interfaces/IEvent'; export class Event { id: string; name: string; date: string; joined: boolean; + deletable: boolean; public assignFromResponse(eventDataResponse: IEvent) { this.id = eventDataResponse.id; @@ -12,6 +13,7 @@ export class Event { const temp = new Date(Number(eventDataResponse.dueDate)); this.date = temp.toLocaleString('en-GB'); this.joined = eventDataResponse.joined; + this.deletable = eventDataResponse.deletable; return this; } diff --git a/src/app/models/friendinfo.ts b/src/app/models/friendinfo.ts index 887737b..637d000 100644 --- a/src/app/models/friendinfo.ts +++ b/src/app/models/friendinfo.ts @@ -1,5 +1,3 @@ -import {Levellist} from 'src/app/models/levellist'; - export class FriendInfo { id: number; name: string; diff --git a/src/app/models/group.ts b/src/app/models/group.ts index 51be07f..4d67937 100644 --- a/src/app/models/group.ts +++ b/src/app/models/group.ts @@ -1,7 +1,8 @@ -import {User} from 'src/app/models/user'; -import {Event} from 'src/app/models/event'; -import {IGroup} from './interfaces/IGroup'; -import {environment} from 'src/environments/environment'; +import { User } from 'src/app/models/user'; +import { Event } from 'src/app/models/event'; +import { IGroup } from './interfaces/IGroup'; +import { environment } from 'src/environments/environment'; +import { IUser } from './interfaces/IUser'; export class Group { id: number; @@ -32,10 +33,7 @@ export class Group { } } if (groupDataResponse.admins) { - for (const admin of groupDataResponse.admins) { - user = new User(); - this.admins.push(user.assignFromResponse(admin)); - } + this.updateAdmins(groupDataResponse.admins); } if (groupDataResponse.events) { for (const event of groupDataResponse.events) { @@ -48,6 +46,21 @@ export class Group { return this; } + public updateAdmins(admins: IUser[]) { + this.admins = []; + for (const admin of admins) { + const user = new User(); + this.admins.push(user.assignFromResponse(admin)); + } + for (const admin of this.admins) { + for (const member of this.members) { + if (member.userID === admin.userID) { + member.isGroupAdmin = true; + } + } + } + } + buildPictureUrl(path: string): string { if (path) { return environment.greenvironmentUrl + path; diff --git a/src/app/models/interfaces/IEvent.ts b/src/app/models/interfaces/IEvent.ts index cdf7fa0..49f1c2a 100644 --- a/src/app/models/interfaces/IEvent.ts +++ b/src/app/models/interfaces/IEvent.ts @@ -7,4 +7,6 @@ export interface IEvent { dueDate: string; joined: boolean; + + deletable: boolean; } diff --git a/src/app/models/levellist.ts b/src/app/models/levellist.ts index 09be30c..b510103 100644 --- a/src/app/models/levellist.ts +++ b/src/app/models/levellist.ts @@ -1,23 +1,27 @@ -export class Levellist { - levels: { level: number, name: string, points: number }[] = [ - {level: 0, name: 'Green Horn', points: 0}, - {level: 1, name: 'Good Willed', points: 100}, - {level: 2, name: 'Helper', points: 200}, - {level: 3, name: 'World Saver', points: 300}, - {level: 4, name: 'Hero of the Green Country', points: 400}, - {level: 5, name: 'Champion of the Earth', points: 500}, - {level: 6, name: 'Intergallactic Superhero', points: 600}, - ]; +export class Level { + id: number; + name: string; + levelNumber: number; + points: number; - getLevelName(level: number): any { - let name = 'not defined'; + constructor(id: number, name: string, levelNumber: number, points: number) { + this.id = id; + this.name = name; + this.levelNumber = levelNumber; + this.points = points; + } +} +export class LevelList { + levels: Level[] = []; + + getLevelName(level: number): string { + let name = 'not defined'; for (const rank of this.levels) { - if (level === rank.level) { + if (level === rank.levelNumber) { name = rank.name; } } return name; } - } diff --git a/src/app/models/reportReason.ts b/src/app/models/reportReason.ts new file mode 100644 index 0000000..ba059d0 --- /dev/null +++ b/src/app/models/reportReason.ts @@ -0,0 +1,11 @@ +export class ReportReason { + id: number; + name: string; + description: string; + + constructor(id: number, name: string, describtion: string) { + this.id = id; + this.name = name; + this.description = describtion; + } +} \ No newline at end of file diff --git a/src/app/models/user.ts b/src/app/models/user.ts index ab1d208..09fddca 100644 --- a/src/app/models/user.ts +++ b/src/app/models/user.ts @@ -1,9 +1,9 @@ -import {FriendRequest} from 'src/app/models/friendRequest'; -import {FriendInfo} from 'src/app/models/friendinfo'; -import {GroupInfo} from 'src/app/models/groupinfo'; -import {Post} from 'src/app/models/post'; -import {IUser} from './interfaces/IUser'; -import {environment} from 'src/environments/environment'; +import { FriendRequest } from 'src/app/models/friendRequest'; +import { FriendInfo } from 'src/app/models/friendinfo'; +import { GroupInfo } from 'src/app/models/groupinfo'; +import { Post } from 'src/app/models/post'; +import { IUser } from './interfaces/IUser'; +import { environment } from 'src/environments/environment'; export class User { loggedIn = false; @@ -19,6 +19,7 @@ export class User { friendCount: number; groupCount: number; isAdmin: boolean = false; + isGroupAdmin: boolean; darkmode = false; diff --git a/src/app/services/activity/activity.service.ts b/src/app/services/activity/activity.service.ts index df2246d..1ca7f6e 100644 --- a/src/app/services/activity/activity.service.ts +++ b/src/app/services/activity/activity.service.ts @@ -1,18 +1,51 @@ -import {Injectable} from '@angular/core'; -import {BehaviorSubject} from 'rxjs'; -import {Activity, Activitylist} from 'src/app/models/activity'; -import {environment} from 'src/environments/environment'; -import {Http} from '@angular/http'; +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { Activity, Activitylist } from 'src/app/models/activity'; +import { Level, LevelList } from 'src/app/models/levellist'; +import { ReportReason } from 'src/app/models/reportReason'; +import { environment } from 'src/environments/environment'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { BaseService } from '../base.service'; @Injectable({ providedIn: 'root' }) -export class ActivityService { +export class ActivityService extends BaseService { public activitylist = new BehaviorSubject(new Activitylist()); + public levelList = new BehaviorSubject(new LevelList()); + public reportReasonList = new BehaviorSubject(new Array()); - constructor(private http: Http) { + constructor(http: HttpClient) { + super(http); + } + + private static buildGetActivityBody(): any { + const body = { + query: `query{getActivities{ + id name description points + }}`, variables: {} + }; + return body; + } + + private static buildGetLevelsBody(): any { + const body = { + query: `query{getLevels{ + id name levelNumber points + }}`, variables: {} + }; + return body; + } + + private static buildGetReportReasonsBody(): any { + const body = { + query: `query{getReportReasons { + id name description + }}`, variables: {} + }; + return body; } changeUserInfo(pActivitylist: Activitylist) { @@ -21,22 +54,35 @@ export class ActivityService { public getActivities() { if (this.activitylist.getValue().Actions.length < 1) { - const headers = new Headers(); - headers.set('Content-Type', 'application/json'); - this.http.post(environment.graphQLUrl, this.buildJson()).subscribe(result => { - // push onto subject - this.activitylist.next(this.renderActivity(result.json())); - }); + this.http.post(environment.graphQLUrl, ActivityService.buildGetActivityBody(), { headers: this.headers }) + .pipe(this.retryRated()) + .subscribe(result => { + // push onto subject + this.activitylist.next(this.renderActivity(result)); + }); } } - public buildJson(): any { - const body = { - query: `query{getActivities{ - id name description points - }}`, variables: {} - }; - return body; + public getLevels() { + if (this.levelList.getValue().levels.length < 1) { + this.http.post(environment.graphQLUrl, ActivityService.buildGetLevelsBody(), { headers: this.headers }) + .pipe(this.retryRated()) + .subscribe(result => { + // push onto subject + this.levelList.next(this.renderLevels(result)); + }); + } + } + + public getReportReasons() { + if (this.reportReasonList.getValue().length < 1) { + this.http.post(environment.graphQLUrl, ActivityService.buildGetReportReasonsBody(), { headers: this.headers }) + .pipe(this.retryRated()) + .subscribe(result => { + // push onto subject + this.reportReasonList.next(this.renderReportReasons(result)); + }); + } } public renderActivity(response: any): Activitylist { @@ -50,5 +96,28 @@ export class ActivityService { } return activitylist; } + + public renderLevels(response: any): LevelList { + const levelList = new LevelList(); + for (const level of response.data.getLevels) { + levelList.levels.push(new Level( + level.id, + level.name, + level.levelNumber, + level.points)); + } + return levelList; + } + + public renderReportReasons(response: any): ReportReason[] { + const reportReasons: ReportReason[] = new Array(); + for (const reason of response.data.getReportReasons) { + reportReasons.push(new ReportReason( + reason.id, + reason.name, + reason.description)); + } + return reportReasons; + } } diff --git a/src/app/services/feed/feed.service.ts b/src/app/services/feed/feed.service.ts index 9f0a8fd..0d29177 100644 --- a/src/app/services/feed/feed.service.ts +++ b/src/app/services/feed/feed.service.ts @@ -1,14 +1,15 @@ -import {Injectable} from '@angular/core'; -import {HttpClient, HttpErrorResponse} from '@angular/common/http'; -import {Post} from 'src/app/models/post'; -import {Author} from 'src/app/models/author'; -import {environment} from 'src/environments/environment'; -import {Activity} from 'src/app/models/activity'; -import {BehaviorSubject, Observable} from 'rxjs'; -import {tap} from 'rxjs/operators'; -import {BaseService} from '../base.service'; -import {formatDate} from '@angular/common'; -import {IFileUploadResult} from '../../models/interfaces/IFileUploadResult'; +import { Injectable } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Post } from 'src/app/models/post'; +import { Author } from 'src/app/models/author'; +import { environment } from 'src/environments/environment'; +import { Activity } from 'src/app/models/activity'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { BaseService } from '../base.service'; +import { formatDate } from '@angular/common'; +import { IFileUploadResult } from '../../models/interfaces/IFileUploadResult'; +import { IErrorResponse } from 'src/app/models/interfaces/IErrorResponse'; const createPostGqlQuery = `mutation($content: String!, $type: PostType) { createPost(content: $content, type: $type) { @@ -70,6 +71,12 @@ const downvotePostGqlQuery = `mutation($postId: ID!) { } }`; +const reportPostGqlQuery = `mutation($reasonId: ID!, $postId: ID!) { + reportPost(postId: $postId, reasonId: $reasonId) { + id + } +}`; + const getPostsGqlQuery = `query($first: Int, $offset: Int, $sort: SortType){ getPosts (first: $first, offset: $offset, sort: $sort) { id, @@ -98,6 +105,11 @@ export enum Sort { NEW = 'NEW', TOP = 'TOP', } +export class PostingState { + posting = false; + errorOccured = false; + errorMessage = 'An error occured.'; +} @Injectable({ providedIn: 'root' @@ -109,7 +121,7 @@ export class FeedService extends BaseService { } public postsAvailable = new BehaviorSubject(true); - public posting = new BehaviorSubject(false); + public postingState = new BehaviorSubject(new PostingState()); public posts: BehaviorSubject = new BehaviorSubject([]); private activePostList: Sort = Sort.NEW; private offset = 0; @@ -187,41 +199,73 @@ export class FeedService extends BaseService { * @param file - a file that is being uploaded with the post */ private createPostRequest(body: { variables: any; query: string }, file?: File) { - this.posting.next(true); + this.setPostingState(true); if (file) { return this.postGraphql(body, null, 0) - .pipe(tap(response => { - const updatedPosts = this.posts.getValue(); - const post = this.constructPost(response); - this.uploadPostImage(post.id, file).subscribe((result) => { - if (this.activePostList === Sort.NEW) { - post.mediaUrl = result.fileName; - post.mediaType = result.fileName.endsWith('.png') ? 'IMAGE' : 'VIDEO'; - updatedPosts.unshift(post); - this.posts.next(updatedPosts); - this.posting.next(false); - } + .pipe(tap(response => { + const updatedPosts = this.posts.getValue(); + const post = this.constructPost(response); + this.uploadPostImage(post.id, file).subscribe((result) => { + if (result.success) { + if (this.activePostList === Sort.NEW) { + post.mediaUrl = result.fileName; + post.mediaType = result.fileName.endsWith('.png') ? 'IMAGE' : 'VIDEO'; + updatedPosts.unshift(post); + this.posts.next(updatedPosts); + this.setPostingState(false); + } + } else { + console.error(result.error); + this.setPostingError(result.error); + this.deletePost(post.id).subscribe(); + } }, error => { console.error(error); - this.posting.next(false); - this.deletePost(post.id); + this.setPostingError(error); + this.deletePost(post.id).subscribe(); }); + }, (error: IErrorResponse) => { + this.setPostingError(error.error.errors[0].message); } - )); + )); } else if (!file) { return this.postGraphql(body, null, 0) - .pipe(tap(response => { - this.posting.next(false); - const updatedPosts = this.posts.getValue(); - if (this.activePostList === Sort.NEW) { - const post = this.constructPost(response); - updatedPosts.unshift(post); - this.posts.next(updatedPosts); - } - })); + .pipe(tap(response => { + this.setPostingState(false); + const updatedPosts = this.posts.getValue(); + if (this.activePostList === Sort.NEW) { + const post = this.constructPost(response); + updatedPosts.unshift(post); + this.posts.next(updatedPosts); + } + }, (error: IErrorResponse) => { + console.log(error); + this.setPostingError(error.error.errors[0].message); + })); } } + setPostingState(b: boolean) { + if (b) { + this.postingState.getValue().posting = true; + this.postingState.getValue().errorOccured = false; + this.postingState.getValue().errorMessage = ''; + this.postingState.next(this.postingState.getValue()); + } else { + this.postingState.getValue().posting = false; + this.postingState.getValue().errorOccured = false; + this.postingState.getValue().errorMessage = ''; + this.postingState.next(this.postingState.getValue()); + } + } + + setPostingError(error: string) { + this.postingState.getValue().posting = false; + this.postingState.getValue().errorOccured = true; + this.postingState.getValue().errorMessage = error; + this.postingState.next(this.postingState.getValue()); + } + /** * Uploads a file for a post * @param postId @@ -262,6 +306,22 @@ export class FeedService extends BaseService { return this.postGraphql(body); } + /** + * reports a post + * @param postId + * @param reasonId + */ + public reportPost(reasonId: number, postId: number): any { + const body = { + query: reportPostGqlQuery, variables: { + postId, + reasonId + } + }; + + return this.postGraphql(body); + } + /** * Deletes a post * @param pPostID @@ -274,7 +334,7 @@ export class FeedService extends BaseService { postId: pPostID } }; - return this.http.post(environment.graphQLUrl, body, {headers: this.headers}) + return this.http.post(environment.graphQLUrl, body, { headers: this.headers }) .pipe(this.retryRated()); } @@ -287,7 +347,7 @@ export class FeedService extends BaseService { this.postsAvailable.next(true); this.posts.next([]); return this.http.post(environment.graphQLUrl, FeedService.buildGetPostBody(sort, 0), - {headers: this.headers}) + { headers: this.headers }) .pipe(this.retryRated()) .subscribe(response => { this.posts.next(this.constructAllPosts(response.data.getPosts)); @@ -301,7 +361,7 @@ export class FeedService extends BaseService { public getNextPosts() { this.offset += this.offsetStep; const body = FeedService.buildGetPostBody(this.activePostList, this.offset); - this.http.post(environment.graphQLUrl, body, {headers: this.headers}) + this.http.post(environment.graphQLUrl, body, { headers: this.headers }) .pipe(this.retryRated()) .subscribe(response => { const posts = this.constructAllPosts(response.data.getPosts); diff --git a/src/app/services/group/group.service.ts b/src/app/services/group/group.service.ts index 48beabd..0b427a0 100644 --- a/src/app/services/group/group.service.ts +++ b/src/app/services/group/group.service.ts @@ -1,14 +1,14 @@ -import {Injectable} from '@angular/core'; -import {HttpClient} from '@angular/common/http'; -import {environment} from 'src/environments/environment'; -import {User} from 'src/app/models/user'; -import {Event} from 'src/app/models/event'; -import {BehaviorSubject} from 'rxjs'; -import {Group} from 'src/app/models/group'; -import {tap} from 'rxjs/operators'; -import {BaseService} from '../base.service'; -import {IFileUploadResult} from '../../models/interfaces/IFileUploadResult'; -import {DatasharingService} from 'src/app/services/datasharing.service'; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from 'src/environments/environment'; +import { User } from 'src/app/models/user'; +import { Event } from 'src/app/models/event'; +import { BehaviorSubject } from 'rxjs'; +import { Group } from 'src/app/models/group'; +import { tap } from 'rxjs/operators'; +import { BaseService } from '../base.service'; +import { IFileUploadResult } from '../../models/interfaces/IFileUploadResult'; +import { DatasharingService } from 'src/app/services/datasharing.service'; const getGroupGraphqlQuery = `query($groupId: ID!) { getGroup(groupId:$groupId){ @@ -20,7 +20,7 @@ const getGroupGraphqlQuery = `query($groupId: ID!) { creator{id name handle} admins{id name handle} members{id name handle profilePicture} - events{id name dueDate joined} + events{id name dueDate joined deletable} } }`; @@ -40,14 +40,14 @@ export class GroupService extends BaseService { */ private static buildGetGroupBody(id: string): any { return { - query: getGroupGraphqlQuery, variables: {groupId: id} + query: getGroupGraphqlQuery, variables: { groupId: id } }; } public getGroupData(groupId: string) { const url = environment.graphQLUrl; - return this.http.post(url, GroupService.buildGetGroupBody(groupId), {headers: this.headers}) + return this.http.post(url, GroupService.buildGetGroupBody(groupId), { headers: this.headers }) .pipe(this.retryRated()) .pipe(tap(response => { const group_ = new Group(); @@ -64,6 +64,7 @@ export class GroupService extends BaseService { name dueDate joined + deletable } }`, variables: { name: name, @@ -73,13 +74,58 @@ export class GroupService extends BaseService { }; return this.postGraphql(body, null, 0) - .pipe(tap(response => { - const event = new Event(); - event.assignFromResponse(response.data.createEvent); - const group = this.group.getValue(); - group.events.push(event); - this.group.next(group); - })); + .pipe(tap(response => { + const event = new Event(); + event.assignFromResponse(response.data.createEvent); + const group = this.group.getValue(); + group.events.push(event); + this.group.next(group); + })); + } + + + public addGroupAdmin(userId: string, groupId: string) { + const body = { + query: `mutation($groupId: ID!, $userId: ID!) { + addGroupAdmin(groupId: $groupId, userId: $userId) { + admins{id name handle} + } + }`, variables: { + userId, + groupId + } + }; + const group = this.group.getValue(); + group.admins = []; + this.group.next(group); + return this.postGraphql(body, null, 0) + .pipe(tap(response => { + const group = this.group.getValue(); + group.updateAdmins(response.data.addGroupAdmin.admins); + this.group.next(group); + })); + } + + public removeGroupAdmin(userId: string, groupId: string) { + const body = { + query: `mutation($groupId: ID!, $userId: ID!) { + removeGroupAdmin(groupId: $groupId, userId: $userId) { + admins{id name handle} + } + }`, variables: { + userId, + groupId + } + }; + const group = this.group.getValue(); + group.admins = []; + this.group.next(group); + return this.postGraphql(body, null, 0) + .pipe(tap(response => { + const group = this.group.getValue(); + group.updateAdmins(response.data.removeGroupAdmin.admins); + this.group.next(group); + })); } public joinEvent(eventId: string) { @@ -97,8 +143,6 @@ export class GroupService extends BaseService { } public leaveEvent(eventId: string) { - const headers = new Headers(); - headers.set('Content-Type', 'application/json'); const body = { query: `mutation($eventId: ID!) { leaveEvent(eventId: $eventId) { @@ -108,7 +152,29 @@ export class GroupService extends BaseService { eventId: eventId } }; - return this.postGraphql(body); + return this.postGraphql(body) + .pipe(this.retryRated()); + } + + public deleteEvent(eventId: string) { + const body = { + query: `mutation($eventId: ID!) { + deleteEvent(eventId: $eventId) + }`, variables: { + eventId + } + }; + return this.postGraphql(body) + .pipe(this.retryRated()) + .pipe(tap(response => { + const group = this.group.getValue(); + for (let i = 0; i < group.events.length; i++) { + if (group.events[i].id === eventId) { + group.events.splice(i, 1); + } + } + this.group.next(group); + })); } public changeProfilePicture(file: any, id: number) { @@ -127,9 +193,9 @@ export class GroupService extends BaseService { } }; return this.postGraphql(body) - .pipe(tap(response => { - this.data.deleteGroup(groupId); - })); + .pipe(tap(response => { + this.data.deleteGroup(groupId); + })); } public leaveGroup(groupId: number) { @@ -141,8 +207,8 @@ export class GroupService extends BaseService { } }; return this.postGraphql(body) - .pipe(tap(response => { - this.data.deleteGroup(groupId); - })); + .pipe(tap(response => { + this.data.deleteGroup(groupId); + })); } }
level {{level.level}} {{level.levelNumber}} level name