Merge branch 'master' of Software_Engineering_I/greenvironment-frontend into max_dev

master
Max_ES 5 years ago committed by Gitea
commit ccaf90b82a

@ -29,7 +29,8 @@
],
"styles": [
"src/styles/greenvironment-material-theme.scss",
"src/styles.sass"
"src/styles.sass",
"./node_modules/ngx-lightbox/lightbox.css"
],
"scripts": []
},

@ -21,6 +21,7 @@
"resources": {
"files": [
"/assets/**",
"/data/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}

29
package-lock.json generated

@ -3136,9 +3136,9 @@
}
},
"abbrev": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
"integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"accepts": {
"version": "1.3.5",
@ -6931,9 +6931,9 @@
}
},
"globule": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
"integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.0.tgz",
"integrity": "sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg==",
"requires": {
"glob": "~7.1.1",
"lodash": "~4.17.10",
@ -9104,6 +9104,11 @@
"opencollective-postinstall": "^2.0.2"
}
},
"ngx-lightbox": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ngx-lightbox/-/ngx-lightbox-2.1.1.tgz",
"integrity": "sha512-mI1hUo/DrhcTeWi/7AVusKfzLr5ySz+EFrOksNlCEiaQKVMqCZdUgj+7ruKDROF7dZcNkJL9Wf99yyiG2nhezQ=="
},
"ngx-socket-io": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ngx-socket-io/-/ngx-socket-io-2.1.1.tgz",
@ -9223,9 +9228,9 @@
}
},
"node-sass": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz",
"integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
"integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
@ -10944,9 +10949,9 @@
}
},
"rxjs": {
"version": "6.3.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
"integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
"integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
"requires": {
"tslib": "^1.9.0"
}

@ -35,9 +35,10 @@
"hammerjs": "^2.0.8",
"js-sha512": "^0.8.0",
"ngx-infinite-scroll": "^8.0.1",
"ngx-lightbox": "^2.1.1",
"ngx-socket-io": "^2.1.1",
"node-sass": "^4.13.0",
"rxjs": "~6.3.3",
"node-sass": "^4.13.1",
"rxjs": "~6.5.4",
"ts-md5": "^1.2.7",
"zone.js": "^0.8.29"
},

@ -11,10 +11,10 @@ $scrollbar-color-dark: transparent
$scrollbar-thumb-color-dark: #aaa
::ng-deep body
scrollbar-color: $scrollbar-color $scrollbar-thumb-color
scrollbar-color: $scrollbar-thumb-color $scrollbar-color
scrollbar-width: thin
.dark-theme
scrollbar-color: $scrollbar-color-dark $scrollbar-thumb-color-dark
scrollbar-color: $scrollbar-thumb-color-dark $scrollbar-color-dark
::ng-deep ::-webkit-scrollbar
width: 4px

@ -21,11 +21,4 @@ describe('AppComponent', () => {
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('greenvironment');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to greenvironment!');
});
});

@ -63,6 +63,7 @@ import {MatNativeDateModule, MatProgressBarModule} from '@angular/material/';
import {MatSnackBarModule} from '@angular/material/snack-bar';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import {LightboxModule} from 'ngx-lightbox';
const config: SocketIoConfig = {url: 'http://localhost:4444', options: {}};
@ -147,6 +148,7 @@ const appRoutes: Routes = [
MatDatepickerModule,
MatSnackBarModule,
MatProgressBarModule,
LightboxModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
],
entryComponents: [

@ -1,6 +1,6 @@
<mat-card class="post" *ngFor="let post of childPostList" [class.selected]="post === selectedPost" tabindex="0">
<mat-card-header>
<div mat-card-avatar>
<div mat-card-avatar (click)="showUserProfile(this.post)">
<img class="profile-picture" [src]="post.author.profilePicture"/>
</div>
<div id="button-box">
@ -14,7 +14,7 @@
</mat-menu>
</div>
<mat-card-title>
{{post.author.name}}
<span class="postlistUsername" (click)="showUserProfile(this.post)">{{post.author.name}}</span>
<a class="mat-card-subtitle" (click)="showUserProfile(this.post)">@{{post.author.handle}}</a>
<p class="mat-card-subtitle">&nbsp; {{post.date}}</p>
</mat-card-title>
@ -25,17 +25,24 @@
<p [innerHTML]="post.htmlContent"></p>
</mat-card-content>
<mat-card-actions>
<button mat-button (click)="voteUp(post)" matTooltip="vote up" matTooltipShowDelay="500">
<mat-icon class="voteButton voted" aria-hidden="false" color="primary" *ngIf="post.userVote == 'UPVOTE'">thumb_up</mat-icon>
<mat-icon class="voteButton" aria-hidden="false" *ngIf="!post.userVote || post.userVote == 'DOWNVOTE'">thumb_up</mat-icon>
</button>
{{post.upvotes}}
<button mat-button (click)="voteDown(post)" matTooltip="vote down" matTooltipShowDelay="500">
<mat-icon class="voteButton voted" aria-hidden="false" color="primary" *ngIf="post.userVote == 'DOWNVOTE'">thumb_down</mat-icon>
<mat-icon class="voteButton" aria-hidden="false" *ngIf="!post.userVote || post.userVote == 'UPVOTE'">thumb_down</mat-icon>
</button>
{{post.downvotes}}
<div *ngIf="post.activity" class="activity-info">
<div *ngIf="post.activity" class="activity-info" fxHide.gt-sm="true">
<span class="span" [matTooltip]="post.activity.description" matTooltipShowDelay="200">
{{post.activity.points}} points earned through <b>{{post.activity.name}}</b>
</span>
</div>
<div class="postVoteButtons">
<button mat-button (click)="voteUp(post)" matTooltip="vote up" matTooltipShowDelay="500">
<mat-icon class="voteButton voted" aria-hidden="false" color="primary" *ngIf="post.userVote == 'UPVOTE'">thumb_up</mat-icon>
<mat-icon class="voteButton" aria-hidden="false" *ngIf="!post.userVote || post.userVote == 'DOWNVOTE'">thumb_up</mat-icon>
</button>
<div class="voteCount">{{post.upvotes}}</div>
<button mat-button (click)="voteDown(post)" matTooltip="vote down" matTooltipShowDelay="500">
<mat-icon class="voteButton voted" aria-hidden="false" color="primary" *ngIf="post.userVote == 'DOWNVOTE'">thumb_down</mat-icon>
<mat-icon class="voteButton" aria-hidden="false" *ngIf="!post.userVote || post.userVote == 'UPVOTE'">thumb_down</mat-icon>
</button>
<div class="voteCount">{{post.downvotes}}</div>
</div>
<div *ngIf="post.activity" class="activity-info" fxHide.lt-md="true">
<span class="span" [matTooltip]="post.activity.description" matTooltipShowDelay="200">
{{post.activity.points}} points earned through <b>{{post.activity.name}}</b>
</span>

@ -17,7 +17,7 @@ $mat-card-header-size: 40px !default
.mat-card-content
overflow: auto
margin: 0 1em
margin: 0.5em 1em 0
::ng-deep a
color: $primary-color
.mat-card-actions
@ -26,8 +26,14 @@ $mat-card-header-size: 40px !default
::ng-deep p
margin: 0
a:hover
.postlistUsername, a
cursor: pointer
margin-right: 0.4em
&:hover
text-decoration: underline
.postVoteButtons
display: inline-block
#button-box
text-align: right
@ -61,6 +67,7 @@ $mat-card-header-size: 40px !default
border-radius: 50%
width: $mat-card-header-size
height: $mat-card-header-size
cursor: pointer
.activity-info
display: contents
@ -74,4 +81,28 @@ $mat-card-header-size: 40px !default
&.voted
scale: 0.9
.voteCount
display: inline
@media (max-width: 959px)
.activity-info > .span
margin-left: 1em
width: calc(100% - 1em)
display: block
text-align: left
.postVoteButtons
text-align: end
margin-right: 0.5em
display: flex
align-items: center
.voteButton
scale: 1
&.voted
scale: 1.1
.mat-card-actions
display: flex
justify-content: end
align-items: center

@ -3,11 +3,11 @@
<!--on small screen-->
<mat-toolbar id="toolbar" fxShow="true" fxHide.gt-sm="true">
<mat-toolbar-row>
<div class="hover-box" matTooltip="upload new picture" *ngIf="ownProfile" (click)="openFileUploadDialog()">
<div class="hover-box profile-picture-container" matTooltip="upload new picture" *ngIf="ownProfile" (click)="openFileUploadDialog()">
<img class="profile-picture" [src]="userProfile.profilePicture"/>
<mat-icon id="icon">camera_alt</mat-icon>
</div>
<div *ngIf="!ownProfile">
<div class="profile-picture-container" *ngIf="!ownProfile" (click)="openPfpLightbox()">
<img class="profile-picture" [src]="userProfile.profilePicture"/>
</div>
@ -44,12 +44,12 @@
<!--on big screen-->
<mat-toolbar id="toolbar" fxShow="true" fxHide.lt-md="true">
<mat-toolbar-row>
<div class="hover-box" matTooltip="upload new picture" *ngIf="ownProfile" (click)="openFileUploadDialog()">
<div class="hover-box profile-picture-container" matTooltip="upload new picture" *ngIf="ownProfile" (click)="openFileUploadDialog()">
<img class="profile-picture" [src]="userProfile.profilePicture"/>
<mat-icon id="icon">camera_alt</mat-icon>
</div>
<div *ngIf="!ownProfile">
<img class="profile-picture" [src]="userProfile.profilePicture"/>
<div class="profile-picture-container" *ngIf="!ownProfile" (click)="openPfpLightbox()">
<img class="profile-picture" [src]="userProfile.profilePicture" />
</div>
<span id="username">{{userProfile.username}}</span>
<span id="handle">@{{userProfile.handle}}</span>

@ -54,8 +54,11 @@
color: white
$mat-card-header-size: 100px !default
.hover-box
.profile-picture-container
cursor: pointer
z-index: 11
.hover-box
text-align: center
display: flex
justify-content: center

@ -9,6 +9,7 @@ import {HttpClient} from '@angular/common/http';
import {SelfService} from '../../services/selfservice/self.service';
import {MatDialog} from '@angular/material';
import {DialogFileUploadComponent} from './fileUpload/fileUpload.component';
import {Lightbox} from 'ngx-lightbox';
@Component({
selector: 'app-profile',
@ -32,7 +33,7 @@ export class ProfileComponent implements OnInit {
private requestService: RequestService,
private data: DatasharingService,
private profileService: ProfileService,
private selfService: SelfService,
private lightbox: Lightbox,
public dialog: MatDialog) {
router.events.forEach((event) => {
// check if the user is on the profile page (of userY) and routes to the page of userY (e.g. his own page)
@ -85,4 +86,15 @@ export class ProfileComponent implements OnInit {
}
});
}
/**
* Opens a lightbox with the users profile picture
*/
openPfpLightbox() {
this.lightbox.open([{
src: this.userProfile.profilePicture,
caption: `${this.userProfile.username}'s profile picture`,
thumb: this.userProfile.profilePicture,
}], 0, {disableScrolling: true});
}
}

@ -7,6 +7,7 @@ import {Activity} from 'src/app/models/activity';
import {BehaviorSubject} from 'rxjs';
import {tap} from 'rxjs/operators';
import {BaseService} from '../base.service';
import {formatDate} from '@angular/common';
const createPostGqlQuery = `mutation($content: String!) {
createPost(content: $content) {
@ -125,6 +126,15 @@ export class FeedService extends BaseService {
};
}
/**
* Returns if the input date is today
* @param date
*/
private static dateIsToday(date: Date) {
date = new Date(date);
return new Date().setHours(0, 0, 0, 0) === date.setHours(0, 0, 0, 0);
}
/**
* Creates a new post
* @param pContent
@ -240,8 +250,13 @@ export class FeedService extends BaseService {
.pipe(this.retryRated())
.subscribe(response => {
const posts = this.constructAllPosts(response);
const updatedPostList = this.posts.getValue().concat(posts);
this.posts.next(updatedPostList);
const previousPosts = this.posts.getValue();
for (const post of previousPosts.reverse()) {
if (!posts.find(p => p.id === post.id)) {
posts.unshift(post);
}
}
this.posts.next(posts);
if (posts.length < this.offsetStep) {
this.postsAvailable.next(false);
}
@ -257,8 +272,13 @@ export class FeedService extends BaseService {
profilePicture = 'assets/images/default-profilepic.svg';
}
const author = new Author(post.author.id, post.author.name, post.author.handle, profilePicture);
const temp = new Date(Number(post.createdAt));
const date = temp.toLocaleString('en-GB');
const postDate = new Date(Number(post.createdAt));
let date: string;
if (FeedService.dateIsToday(postDate)) {
date = formatDate(postDate, 'HH:mm', 'en-GB');
} else {
date = formatDate(postDate, 'dd.MM.yy HH:mm', 'en-GB');
}
let activity: Activity;
if (post.activity) {
activity = new Activity(
@ -293,8 +313,13 @@ export class FeedService extends BaseService {
profilePicture = 'assets/images/default-profilepic.svg';
}
const author = new Author(post.author.id, post.author.name, post.author.handle, profilePicture);
const temp = new Date(Number(post.createdAt));
const date = temp.toLocaleString('en-GB');
const postDate = new Date(Number(post.createdAt));
let date: string;
if (FeedService.dateIsToday(postDate)) {
date = formatDate(postDate, 'HH:mm', 'en-GB');
} else {
date = formatDate(postDate, 'dd.MM.yy HH:mm', 'en-GB');
}
let activity: Activity;
if (post.activity) {
activity = new Activity(

Loading…
Cancel
Save