...
 
Commits (6)
......@@ -75,7 +75,7 @@ import { AnalyticsModule } from "./modules/analytics/analytics.module";
ReactiveFormsModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot(MindsAppRoutes),
RouterModule.forRoot(MindsAppRoutes, { onSameUrlNavigation: "reload" }),
CaptchaModule,
CommonModule,
AnalyticsModule,
......
......@@ -38,11 +38,6 @@
<li class="mdl-menu__item" *ngIf="!asyncBlockInProgress && !asyncBlock" (click)="block()" i18n="@@COMMON__POST_MENU__BLOCK">Block user</li>
<li class="mdl-menu__item" *ngIf="!asyncBlockInProgress && asyncBlock" (click)="unBlock()" i18n="@@COMMON__POST_MENU__UNBLOCK">Unblock user</li>
</ng-container>
<!-- ALLOW COMMENTS -->
<ng-container *ngIf="featuresService.has('allow-comments-toggle') && options.indexOf('allow-comments') !== -1 && entity.ownerObj.guid == session.getLoggedInUser().guid ">
<li class="mdl-menu__item" *ngIf="!entity.allow_comments" (click)="allowComments(true)" i18n="@@COMMON__POST_MENU__ALLOW_COMMENTS">Allow Comments</li>
<li class="mdl-menu__item" *ngIf="entity.allow_comments" (click)="allowComments(false)" i18n="@@COMMON__POST_MENU__DISABLE_COMMENTS">Disable Comments</li>
</ng-container>
<!-- ADMIN EDIT FLAGS -->
<ng-container *ngIf="options.indexOf('set-explicit') !== -1 && session.isAdmin()">
<li class="mdl-menu__item m-postMenu__item--nsfw">
......
......@@ -15,11 +15,7 @@ import { sessionMock } from '../../../../tests/session-mock.spec';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { BlockListService } from '../../services/block-list.service';
import { ActivityService } from '../../services/activity.service';
import { FeaturesService } from '../../../services/features.service';
import { activityServiceMock } from '../../../../tests/activity-service-mock.spec';
import { storageMock } from '../../../../tests/storage-mock.spec';
import { featuresServiceMock } from '../../../../tests/features-service-mock.spec';
/* tslint:disable */
/* Mock section */
......@@ -99,8 +95,6 @@ describe('PostMenuComponent', () => {
{ provide: Client, useValue: clientMock },
{ provide: Session, useValue: sessionMock },
{ provide: OverlayModalService, useValue: overlayModalServiceMock },
{ provide: ActivityService, useValue: activityServiceMock },
{ provide: FeaturesService, useValue: featuresServiceMock },
{ provide: Storage, useValue: storageMock },
{ provide: BlockListService, useFactory: () => {
return BlockListService._(clientMock, sessionMock, storageMock);
......@@ -116,7 +110,6 @@ describe('PostMenuComponent', () => {
// synchronous beforeEach
beforeEach(() => {
featuresServiceMock.mock('allow-comments-toggle', true);
fixture = TestBed.createComponent(PostMenuComponent);
comp = fixture.componentInstance;
......@@ -124,9 +117,8 @@ describe('PostMenuComponent', () => {
comp.entity = {};
// comp.opened = true;
comp.entity.ownerObj = { guid: '1' };
comp.cardMenuHandler();
comp.cardMenuHandler();
fixture.detectChanges();
});
it('should have dropdown', () => {
......@@ -148,19 +140,4 @@ describe('PostMenuComponent', () => {
fixture.detectChanges();
expect(clientMock.delete.calls.mostRecent().args[0]).toEqual('api/v1/block/1');
});
it('should allow comments', () => {
spyOn(comp.optionSelected, 'emit');
comp.allowComments(true);
expect(activityServiceMock.toggleAllowComments).toHaveBeenCalledWith(comp.entity, true);
expect(comp.entity.allow_comments).toEqual(true);
});
it('should disable comments', () => {
spyOn(comp.optionSelected, 'emit');
comp.allowComments(false);
expect(activityServiceMock.toggleAllowComments).toHaveBeenCalledWith(comp.entity, false);
expect(comp.entity.allow_comments).toEqual(false);
});
});
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { Session } from '../../../services/session';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { Client } from '../../../services/api/client';
import { ReportCreatorComponent } from '../../../modules/report/creator/creator.component';
import { MindsUser } from '../../../interfaces/entities';
import { SignupModalService } from '../../../modules/modals/signup/service';
import { BlockListService } from '../../services/block-list.service';
import { ActivityService } from '../../../common/services/activity.service';
import { FeaturesService } from '../../../services/features.service';
import { BlockListService } from "../../services/block-list.service";
type Option =
'edit'
......@@ -27,8 +26,7 @@ type Option =
| 'subscribe'
| 'unsubscribe'
| 'rating'
| 'block'
| 'allow-comments';
| 'block';
@Component({
moduleId: module.id,
......@@ -37,7 +35,7 @@ type Option =
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PostMenuComponent implements OnInit {
export class PostMenuComponent {
@Input() entity: any;
@Input() options: Array<Option>;
@Output() optionSelected: EventEmitter<Option> = new EventEmitter<Option>();
......@@ -68,13 +66,10 @@ export class PostMenuComponent implements OnInit {
private overlayModal: OverlayModalService,
public signupModal: SignupModalService,
protected blockListService: BlockListService,
protected activityService: ActivityService,
public featuresService: FeaturesService) {
) {
this.initCategories();
}
ngOnInit() {}
initCategories() {
for (let category in window.Minds.categories) {
this.categories.push({
......@@ -334,12 +329,4 @@ export class PostMenuComponent implements OnInit {
this.entity.nsfw = nsfw;
}
async allowComments(areAllowed: boolean) {
this.entity.allow_comments = areAllowed;
const result = await this.activityService.toggleAllowComments(this.entity, areAllowed);
if (result !== areAllowed) {
this.entity.allow_comments = result;
}
}
}
import { EventEmitter, Injectable } from '@angular/core';
import { Client } from '../../services/api/client';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable()
export class ActivityService {
public allowComment$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
constructor(
private client: Client
) {}
public async toggleAllowComments(entity: any, areAllowed: boolean) {
const payload = {
allowed: areAllowed
};
const oldValue = entity['allow_comments'];
try {
await this.client.post(`api/v2/permissions/comments/${entity.guid}`, payload);
this.allowComment$.next(areAllowed);
return areAllowed;
} catch (ex) {
console.error('Error posting activity comment permissions', ex);
return oldValue;
}
}
}
......@@ -5,8 +5,7 @@ import { WireRewardsStruc } from '../modules/wire/interfaces/wire.interfaces';
export interface MindsActivityObject {
activity : Array<any>;
pinned : Array<any>;
allow_comments: boolean;
pinned : Array<any>;
}
export interface MindsBlogEntity {
......@@ -28,7 +27,6 @@ export interface MindsBlogEntity {
time_published?: number;
access_id?: number;
license?: string;
allow_comments: boolean;
}
export interface Message {
......
///<reference path="../../../../../node_modules/@types/jasmine/index.d.ts"/>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, EventEmitter, Input, Output, Pipe, PipeTransform, NO_ERRORS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { CommonModule as NgCommonModule } from '@angular/common';
import { MindsBlogEntity } from '../../../interfaces/entities';
import { BlogView } from './view';
import { SafePipe } from '../../../common/pipes/safe';
import { Client } from '../../../services/api/client';
import { clientMock } from '../../../../tests/client-mock.spec';
import { sessionMock } from '../../../../tests/session-mock.spec';
import { Session } from '../../../services/session';
import { scrollServiceMock } from '../../../../tests/scroll-service-mock.spec';
import { ScrollService } from '../../../services/ux/scroll';
import { mindsTitleMock } from '../../../mocks/services/ux/minds-title.service.mock.spec';
import { MindsTitle } from '../../../services/ux/title';
import { AttachmentService } from '../../../services/attachment';
import { attachmentServiceMock } from '../../../../tests/attachment-service-mock.spec';
import { contextServiceMock } from '../../../../tests/context-service-mock.spec';
import { ContextService } from '../../../services/context.service';
import { AnalyticsService } from '../../../services/analytics';
import { analyticsServiceMock } from '../../../../tests/analytics-service-mock.spec';
import { ActivityService } from '../../../common/services/activity.service';
import { activityServiceMock } from '../../../../tests/activity-service-mock.spec';
describe('Blog view component', () => {
let comp: BlogView;
let fixture: ComponentFixture<BlogView>;
const blog: MindsBlogEntity = {
guid: '1',
title: 'test blog',
description: 'description',
ownerObj: {},
allow_comments: true
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
BlogView,
SafePipe
], // declare the test component
imports: [
NgCommonModule,
RouterTestingModule,
],
providers: [
{ provide: ActivityService, useValue: activityServiceMock },
{ provide: AnalyticsService, useValue: analyticsServiceMock },
{ provide: AttachmentService, useValue: attachmentServiceMock },
{ provide: Client, useValue: clientMock },
{ provide: ContextService, useValue: contextServiceMock },
{ provide: MindsTitle, useValue: mindsTitleMock },
{ provide: ScrollService, useValue: scrollServiceMock },
{ provide: Session, useValue: sessionMock },
],
schemas: [
NO_ERRORS_SCHEMA,
],
})
.overrideProvider( ActivityService, { useValue: activityServiceMock })
.compileComponents(); // compile template and css
}));
// synchronous beforeEach
beforeEach(() => {
fixture = TestBed.createComponent(BlogView);
comp = fixture.componentInstance;
comp.blog = blog;
fixture.detectChanges();
});
});
import { Component, ElementRef, ViewChild, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
import { Component, ElementRef, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { Client } from '../../../services/api';
......@@ -11,9 +11,7 @@ import { MindsBlogEntity } from '../../../interfaces/entities';
import { AttachmentService } from '../../../services/attachment';
import { ContextService } from '../../../services/context.service';
import { optimizedResize } from '../../../utils/optimized-resize';
import { ActivityService } from '../../../common/services/activity.service';
@Component({
moduleId: module.id,
selector: 'm-blog-view',
......@@ -21,11 +19,10 @@ import { ActivityService } from '../../../common/services/activity.service';
host: {
'class': 'm-blog'
},
templateUrl: 'view.html',
providers: [ActivityService]
templateUrl: 'view.html'
})
export class BlogView implements OnInit, OnDestroy {
export class BlogView {
minds;
guid: string;
......@@ -43,10 +40,7 @@ export class BlogView implements OnInit, OnDestroy {
scroll_listener;
menuOptions: Array<string> = ['edit', 'follow', 'feature',
'delete', 'report', 'subscribe',
'set-explicit', 'remove-explicit', 'rating',
'allow-comments'];
menuOptions: Array<string> = ['edit', 'follow', 'feature', 'delete', 'report', 'subscribe', 'set-explicit', 'remove-explicit', 'rating'];
@ViewChild('lockScreen', { read: ElementRef, static: false }) lockScreen;
......@@ -61,9 +55,7 @@ export class BlogView implements OnInit, OnDestroy {
public attachment: AttachmentService,
private context: ContextService,
public analytics: AnalyticsService,
public analyticsService: AnalyticsService,
protected activityService: ActivityService,
private cd: ChangeDetectorRef,
public analyticsService: AnalyticsService
) {
this.minds = window.Minds;
this.element = _element.nativeElement;
......@@ -77,7 +69,7 @@ export class BlogView implements OnInit, OnDestroy {
}
isVisible() {
// listens every 0.6 seconds
//listens every 0.6 seconds
this.scroll_listener = this.scroll.listen((e) => {
const bounds = this.element.getBoundingClientRect();
if (bounds.top < this.scroll.view.clientHeight && bounds.top + bounds.height > this.scroll.view.clientHeight) {
......@@ -121,9 +113,8 @@ export class BlogView implements OnInit, OnDestroy {
}
ngOnDestroy() {
if (this.scroll_listener) {
if (this.scroll_listener)
this.scroll.unListen(this.scroll_listener);
}
}
menuOptionSelected(option: string) {
......@@ -153,9 +144,8 @@ export class BlogView implements OnInit, OnDestroy {
}
calculateLockScreenHeight() {
if (!this.lockScreen) {
if (!this.lockScreen)
return;
}
const lockScreenOverlay = this.lockScreen.nativeElement.querySelector('.m-wire--lock-screen');
if (lockScreenOverlay) {
const rect = lockScreenOverlay.getBoundingClientRect();
......
......@@ -232,7 +232,7 @@
>
<i class="material-icons">reply</i>
<span *ngIf="comment.replies_count > 0">{{ comment.replies_count }} Replies</span>
<span *ngIf="comment.replies_count <= 0 && canReply">Reply</span>
<span *ngIf="comment.replies_count <= 0">Reply</span>
</span>
</div>
......
......@@ -70,7 +70,6 @@ export class CommentComponent implements OnChanges {
translateToggle: boolean = false;
commentAge$: Observable<number>;
@Input() canEdit: boolean = false;
@Input() canReply = true;
@Output() onReply = new EventEmitter();
......
......@@ -232,9 +232,9 @@
(click)="toggleReplies();"
*ngIf="comment.can_reply"
>
<i *ngIf="comment.replies_count > 0 || (activityService.allowComment$ | async)" class="material-icons">reply</i>
<i class="material-icons">reply</i>
<span *ngIf="comment.replies_count > 0">{{ comment.replies_count }} Replies</span>
<span *ngIf="comment.replies_count <= 0 && (activityService.allowComment$ | async)">Reply</span>
<span *ngIf="comment.replies_count <= 0">Reply</span>
</span>
</div>
......
......@@ -9,9 +9,6 @@ import {
OnChanges,
Input,
ElementRef,
OnInit,
OnDestroy,
AfterViewInit
} from '@angular/core';
import { Session } from '../../../services/session';
......@@ -23,9 +20,8 @@ import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { ReportCreatorComponent } from '../../report/creator/creator.component';
import { CommentsListComponent } from '../list/list.component';
import { TimeDiffService } from '../../../services/timediff.service';
import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { ActivityService } from '../../../common/services/activity.service';
import { Observable } from 'rxjs';
import { map } from "rxjs/operators";
@Component({
selector: 'm-comment',
......@@ -44,7 +40,7 @@ import { ActivityService } from '../../../common/services/activity.service';
],
})
export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterViewInit {
export class CommentComponentV2 implements OnChanges {
comment: any;
editing: boolean = false;
......@@ -77,8 +73,6 @@ export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterVi
translationInProgress: boolean;
translateToggle: boolean = false;
commentAge$: Observable<number>;
canReply = true;
@Input() canEdit: boolean = false;
@Input() canDelete: boolean = false;
@Input() hideToolbar: boolean = false;
......@@ -94,8 +88,7 @@ export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterVi
private overlayModal: OverlayModalService,
private cd: ChangeDetectorRef,
private timeDiffService: TimeDiffService,
private el: ElementRef,
protected activityService: ActivityService
private el: ElementRef
) {}
ngOnInit() {
......@@ -104,7 +97,6 @@ export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterVi
}));
}
ngAfterViewInit() {
if (this.comment.focused) {
this.el.nativeElement.scrollIntoView(true);
......@@ -114,13 +106,10 @@ export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterVi
}
}
ngOnDestroy() {}
@Input('comment')
set _comment(value: any) {
if (!value) {
if (!value)
return;
}
this.comment = value;
this.attachment.load(this.comment);
......@@ -132,9 +121,7 @@ export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterVi
}
saveEnabled() {
return !this.inProgress
&& this.canPost
&& ((this.comment.description && this.comment.description.trim() !== '') || this.attachment.has());
return !this.inProgress && this.canPost && ((this.comment.description && this.comment.description.trim() !== '') || this.attachment.has());
}
save() {
......@@ -144,7 +131,7 @@ export class CommentComponentV2 implements OnChanges, OnInit, OnDestroy, AfterVi
return;
}
const data = this.attachment.exportMeta();
let data = this.attachment.exportMeta();
data['comment'] = this.comment.description;
this.editing = false;
......
......@@ -31,8 +31,7 @@
&& !ascendingInProgress
&& !error
&& comments?.length === 0
&& parent.type == 'activity'
&& activityService.allowComment$"
&& parent.type == 'activity'"
i18n="@@MINDS__COMMENTS__START_CONVERSATION"
>
Start the conversation!
......
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, Renderer,
ViewChild,
OnInit,
OnDestroy
ViewChild
} from '@angular/core';
import { Client } from '../../../services/api/client';
......@@ -11,7 +9,6 @@ import { Upload } from '../../../services/api/upload';
import { AttachmentService } from '../../../services/attachment';
import { Textarea } from '../../../common/components/editors/textarea.component';
import { SocketsService } from '../../../services/sockets';
import { ActivityService } from '../../../common/services/activity.service';
@Component({
moduleId: module.id,
......@@ -23,13 +20,12 @@ import { ActivityService } from '../../../common/services/activity.service';
provide: AttachmentService,
useFactory: AttachmentService._,
deps: [Session, Client, Upload]
},
ActivityService
}
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CommentsListComponent implements OnInit, OnDestroy {
export class CommentsListComponent {
minds;
object;
......@@ -65,6 +61,7 @@ export class CommentsListComponent implements OnInit, OnDestroy {
socketSubscriptions: any = {
comment: null
};
error: string;
@Input() conversation: boolean = false;
......@@ -84,8 +81,7 @@ export class CommentsListComponent implements OnInit, OnDestroy {
public attachment: AttachmentService,
public sockets: SocketsService,
private renderer: Renderer,
private cd: ChangeDetectorRef,
public activityService: ActivityService,
private cd: ChangeDetectorRef
) {
this.minds = window.Minds;
}
......@@ -93,18 +89,16 @@ export class CommentsListComponent implements OnInit, OnDestroy {
set _object(value: any) {
this.object = value;
this.guid = this.object.guid;
if (this.object.entity_guid) {
if (this.object.entity_guid)
this.guid = this.object.entity_guid;
}
this.parent = this.object;
}
set _reversed(value: boolean) {
if (value) {
if (value)
this.reversed = true;
} else {
else
this.reversed = false;
}
}
ngOnInit() {
......@@ -149,6 +143,7 @@ export class CommentsListComponent implements OnInit, OnDestroy {
descending: descending,
})
.then((response: any) => {
if (!this.socketRoomName && response.socketRoomName) {
this.socketRoomName = response.socketRoomName;
this.joinSocketRoom();
......@@ -160,7 +155,7 @@ export class CommentsListComponent implements OnInit, OnDestroy {
} else {
this.ascendingInProgress = false;
}
// this.moreDescendingData = true;
//this.moreDescendingData = true;
if (!response.comments) {
if (descending) {
......@@ -173,8 +168,8 @@ export class CommentsListComponent implements OnInit, OnDestroy {
return false;
}
const el = this.scrollView.nativeElement;
const previousScrollHeightMinusTop = el.scrollHeight - el.scrollTop;
let el = this.scrollView.nativeElement;
let previousScrollHeightMinusTop = el.scrollHeight - el.scrollTop;
if (descending) {
this.comments = response.comments.concat(this.comments);
......@@ -244,7 +239,7 @@ export class CommentsListComponent implements OnInit, OnDestroy {
this.overscrollAmount += deltaY;
this.overscrollTimer = setTimeout(() => {
if (this.overscrollAmount < -75) { // 75px
if (this.overscrollAmount < -75) { //75px
this.autoloadPrevious();
}
......@@ -294,9 +289,9 @@ export class CommentsListComponent implements OnInit, OnDestroy {
return;
}
const parent_path = this.parent.child_path || '0:0:0';
const parent_path = this.parent.child_path || "0:0:0";
this.client.get(`api/v1/comments/${this.guid}/${guid}/${parent_path}`, {
this.client.get(`api/v1/comments/${this.guid}/${guid}/${parent_path}`, {
limit: 1,
reversed: false,
descending: true,
......@@ -307,12 +302,10 @@ export class CommentsListComponent implements OnInit, OnDestroy {
}
// if the list is scrolled to the bottom
const scrolledToBottom = this.scrollView.nativeElement.scrollTop
+ this.scrollView.nativeElement.clientHeight >= this.scrollView.nativeElement.scrollHeight;
let scrolledToBottom = this.scrollView.nativeElement.scrollTop + this.scrollView.nativeElement.clientHeight >= this.scrollView.nativeElement.scrollHeight;
if (response.comments[0]._guid == guid) {
if (response.comments[0]._guid == guid)
this.comments.push(response.comments[0]);
}
this.detectChanges();
......@@ -335,14 +328,14 @@ export class CommentsListComponent implements OnInit, OnDestroy {
if (this.session.isLoggedIn() && owner_guid === this.session.getLoggedInUser().guid) {
return;
}
const key = 'thumbs:' + direction + ':count';
let key = 'thumbs:' + direction + ':count';
for (let i = 0; i < this.comments.length; i++) {
if (this.comments[i]._guid == guid) {
this.comments[i][key]++;
this.detectChanges();
}
}
// this.comments = this.comments.slice(0);
//this.comments = this.comments.slice(0);
this.detectChanges();
});
......@@ -357,14 +350,12 @@ export class CommentsListComponent implements OnInit, OnDestroy {
this.detectChanges();
}
}
});
});
}
postEnabled() {
return !this.descendingInProgress
&& !this.ascendingInProgress
&& this.canPost
&& ((this.content && this.content.trim() !== '') || this.attachment.has());
return !this.descendingInProgress && !this.ascendingInProgress && this.canPost && ((this.content && this.content.trim() !== '') || this.attachment.has());
}
keypress(e: KeyboardEvent) {
......@@ -389,11 +380,11 @@ export class CommentsListComponent implements OnInit, OnDestroy {
this.content = this.content.trim();
const data = this.attachment.exportMeta();
let data = this.attachment.exportMeta();
data['comment'] = this.content;
data['parent_path'] = this.parent.child_path || '0:0:0';
const newLength = this.comments.push({ // Optimistic
let newLength = this.comments.push({ // Optimistic
description: this.content,
guid: 0,
ownerObj: this.session.getLoggedInUser(),
......@@ -410,7 +401,7 @@ export class CommentsListComponent implements OnInit, OnDestroy {
this.commentsScrollEmitter.emit('bottom');
try {
const response: any = await this.client.post('api/v1/comments/' + this.guid, data);
let response: any = await this.client.post('api/v1/comments/' + this.guid, data);
this.comments[currentIndex] = response.comment;
} catch (e) {
this.comments[currentIndex].error = (e && e.message) || 'There was an error';
......@@ -509,10 +500,10 @@ export class CommentsListComponent implements OnInit, OnDestroy {
}
getAvatar() {
if (this.session.isLoggedIn()) {
if(this.session.isLoggedIn()) {
return `${this.minds.cdn_url}icon/${this.session.getLoggedInUser().guid}/small/${this.session.getLoggedInUser().icontime}`;
} else {
return `${this.minds.cdn_assets_url}assets/avatars/default-small.png`;
return `${this.minds.cdn_assets_url}assets/avatars/default-small.png`
}
}
......@@ -521,4 +512,8 @@ export class CommentsListComponent implements OnInit, OnDestroy {
this.cd.detectChanges();
}
ngOnChanges(changes) {
// console.log('[comment:list]: on changes', changes);
}
}
......@@ -28,8 +28,7 @@
*ngIf="!inProgress
&& !error
&& comments?.length === 0
&& parent.type == 'activity'
&& (activityService.allowComment$ | async)"
&& parent.type == 'activity'"
i18n="@@MINDS__COMMENTS__START_CONVERSATION"
>
Start the conversation!
......@@ -81,7 +80,6 @@
</div>
<m-comment__poster
*ngIf="(activityService.allowComment$ | async)"
[guid]="guid"
[parent]="parent"
[entity]="entity"
......
......@@ -8,8 +8,6 @@ import {
Output,
Renderer,
ViewChild,
OnInit,
OnDestroy
} from '@angular/core';
import { Client } from '../../../services/api/client';
......@@ -20,10 +18,6 @@ import { Textarea } from '../../../common/components/editors/textarea.component'
import { SocketsService } from '../../../services/sockets';
import { CommentsService } from '../comments.service';
import { BlockListService } from "../../../common/services/block-list.service";
import { ActivityService } from '../../../common/services/activity.service';
import { Subscription } from 'rxjs';
import { TouchSequence } from 'selenium-webdriver';
@Component({
selector: 'm-comments__thread',
......@@ -32,7 +26,7 @@ import { TouchSequence } from 'selenium-webdriver';
providers: [ CommentsService ],
})
export class CommentsThreadComponent implements OnInit {
export class CommentsThreadComponent {
minds;
@Input() parent;
......@@ -66,15 +60,14 @@ export class CommentsThreadComponent implements OnInit {
socketSubscriptions: any = {
comment: null
};
constructor(
public session: Session,
private commentsService: CommentsService,
public sockets: SocketsService,
private renderer: Renderer,
protected blockListService: BlockListService,
private cd: ChangeDetectorRef,
public activityService: ActivityService
private cd: ChangeDetectorRef
) {
this.minds = window.Minds;
}
......@@ -201,8 +194,7 @@ export class CommentsThreadComponent implements OnInit {
const parent_path = this.parent.child_path || "0:0:0";
const scrolledToBottom = this.scrollView.nativeElement.scrollTop
+ this.scrollView.nativeElement.clientHeight >= this.scrollView.nativeElement.scrollHeight;
let scrolledToBottom = this.scrollView.nativeElement.scrollTop + this.scrollView.nativeElement.clientHeight >= this.scrollView.nativeElement.scrollHeight;
try {
let comment: any = await this.commentsService.single({
......@@ -281,6 +273,8 @@ export class CommentsThreadComponent implements OnInit {
}
onPosted({ comment, index }) {
console.log('onPosted called');
console.log(comment, index);
this.comments[index] = comment;
this.detectChanges();
}
......@@ -301,4 +295,8 @@ export class CommentsThreadComponent implements OnInit {
return true;
}
ngOnChanges(changes) {
// console.log('[comment:list]: on changes', changes);
}
}
......@@ -7,8 +7,6 @@ import {
Input,
Output,
Renderer,
OnInit,
OnDestroy
} from '@angular/core';
import {
ActivatedRoute,
......@@ -39,7 +37,7 @@ import { CommentsService } from '../comments.service';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CommentsTreeComponent implements OnInit, OnDestroy {
export class CommentsTreeComponent {
minds;
entity;
......
......@@ -17,12 +17,10 @@ import { HashtagsSelectorComponent } from '../../hashtags/selector/selector.comp
import { VideoChatService } from '../../videochat/videochat.service';
import { UpdateMarkersService } from '../../../common/services/update-markers.service';
import { filter, map, startWith, throttle } from "rxjs/operators";
import { ActivityService } from "../../../common/services/activity.service";
@Component({
selector: 'm-groups--profile',
templateUrl: 'profile.html',
providers: [ActivityService]
templateUrl: 'profile.html'
})
export class GroupsProfile {
......
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Client } from '../../../../services/api';
import { ActivityService } from '../../../../common/services/activity.service';
@Component({
selector: 'minds-button-comment',
inputs: ['_object: object'],
changeDetection: ChangeDetectionStrategy.Default,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<a [ngClass]="{'selected': object['comments:count'] > 0 }">
<i class="material-icons" *ngIf="(activityService.allowComment$ | async) === true">chat_bubble</i>
<i class="material-icons"
*ngIf="(activityService.allowComment$ | async) === false"
title="Comments have been disabled for this post"
i18n-title="@@COMMENTS__DISABLED">
speaker_notes_off
</i>
<i class="material-icons">chat_bubble</i>
<span class="minds-counter" *ngIf="object['comments:count'] > 0">{{object['comments:count'] | number}}</span>
</a>
`
})
export class CommentButton implements OnInit, OnDestroy {
export class CommentButton {
object;
constructor(
public client: Client,
public activityService: ActivityService,
protected cd: ChangeDetectorRef) {
constructor(public client : Client) {
}
ngOnInit() {}
ngOnDestroy() { }
set _object(value: any) {
set _object(value : any){
this.object = value;
this.activityService.allowComment$.next(this.object.allow_comments);
}
}
......@@ -237,7 +237,7 @@
[object]="activity.remind_object ? activity.remind_object : activity"
(done)="wireSubmitted($event)"
></m-wire-button>
<minds-button-comment [object]="activity" (click)="openComments()"></minds-button-comment>
<minds-button-comment [object]="activity" (click)="openComments()"></minds-button-comment>
<minds-button-remind [object]="activity"></minds-button-remind>
<a class="mdl-button mdl-color-text--white mdl-button--colored minds-boost-button"
*ngIf ="session.getLoggedInUser().guid == activity.owner_guid"
......
......@@ -29,7 +29,6 @@ import { NewsfeedService } from "../../../../newsfeed/services/newsfeed.service"
import { ClientMetaService } from "../../../../../common/services/client-meta.service";
import isMobile from '../../../../../helpers/is-mobile';
import { AutocompleteSuggestionsService } from "../../../../suggestions/services/autocomplete-suggestions.service";
import { ActivityService } from '../../../../../common/services/activity.service';
@Component({
moduleId: module.id,
......@@ -39,7 +38,7 @@ import { ActivityService } from '../../../../../common/services/activity.service
},
inputs: ['object', 'commentsToggle', 'focusedCommentGuid', 'visible', 'canDelete', 'showRatingToggle'],
outputs: ['_delete: delete', 'commentsOpened', 'onViewed'],
providers: [ ClientMetaService, ActivityAnalyticsOnViewService, ActivityService ],
providers: [ ClientMetaService, ActivityAnalyticsOnViewService ],
templateUrl: 'activity.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
......@@ -56,7 +55,6 @@ export class Activity implements OnInit {
translateToggle: boolean = false;
translateEvent: EventEmitter<any> = new EventEmitter();
showBoostOptions: boolean = false;
allowComments = true;
@Input() boost: boolean = false;
@Input('boost-toggle')
@Input() showBoostMenuOptions: boolean = false;
......@@ -95,21 +93,12 @@ export class Activity implements OnInit {
get menuOptions(): Array<string> {
if (!this.activity || !this.activity.ephemeral) {
if (this.showBoostMenuOptions) {
return ['edit', 'translate', 'share',
'follow', 'feature', 'delete',
'report', 'set-explicit', 'block',
'rating', 'allow-comments'];
return ['edit', 'translate', 'share', 'follow', 'feature', 'delete', 'report', 'set-explicit', 'block', 'rating'];
} else {
return ['edit', 'translate', 'share',
'follow', 'feature', 'delete',
'report', 'set-explicit', 'block',
'rating', 'allow-comments'];
return ['edit', 'translate', 'share', 'follow', 'feature', 'delete', 'report', 'set-explicit', 'block', 'rating'];
}
} else {
return ['view', 'translate', 'share',
'follow', 'feature', 'report',
'set-explicit', 'block', 'rating',
'allow-comments'];
return ['view', 'translate', 'share', 'follow', 'feature', 'report', 'set-explicit', 'block', 'rating']
}
}
......@@ -130,7 +119,6 @@ export class Activity implements OnInit {
protected newsfeedService: NewsfeedService,
protected clientMetaService: ClientMetaService,
public suggestions: AutocompleteSuggestionsService,
protected activityService: ActivityService,
@SkipSelf() injector: Injector,
elementRef: ElementRef,
) {
......@@ -186,8 +174,6 @@ export class Activity implements OnInit {
this.translationService.isTranslatable(this.activity) ||
(this.activity.remind_object && this.translationService.isTranslatable(this.activity.remind_object))
);
this.allowComments = this.activity.allow_comments;
}
getOwnerIconTime() {
......@@ -266,9 +252,6 @@ export class Activity implements OnInit {
}*/
openComments() {
if (!this.shouldShowComments()) {
return;
}
this.commentsToggle = !this.commentsToggle;
this.commentsOpened.emit(this.commentsToggle);
}
......@@ -361,11 +344,10 @@ export class Activity implements OnInit {
this.translateToggle = true;
break;
}
this.detectChanges();
}
setExplicit(value: boolean) {
const oldValue = this.activity.mature,
let oldValue = this.activity.mature,
oldMatureVisibility = this.activity.mature_visibility;
this.activity.mature = value;
......@@ -435,16 +417,6 @@ export class Activity implements OnInit {
return activity && activity.pending && activity.pending !== '0';
}
/**
* If an activity allow
*/
shouldShowComments() {
return (
this.activity.allow_comments
|| this.activity['comments:count'] >= 0
);
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......
<div class="m-mediaModal mdl-shadow--4dp">
<div class="m-mediaModal__theater">
<!-- This is the element that goes into fullscreen -->
<!-- <div class="m-mediaModal__theater"> -->
<!-- Media: image -->
<!-- This is the element that goes into fullscreen -->
<div class="m-mediaModal__stage m-mediaModal__stage--image" *ngIf="entity.modal_subtype === 'image'">
<div class="m-mediaModal__theater m-mediaModal__theater--image" *ngIf="entity.modal_subtype === 'image'">
<img [src]="entity.thumbnail" (load)="inProgress = !inProgress"/>
<div class="mdl-spinner mdl-js-spinner is-active" [mdl] *ngIf="inProgress"></div>
<div class="m-mediaModal__stageOverlay">
<a class="m-mediaModal__stageOverlayTitle" [routerLink]="['/media', entity.entity_guid]">{{title}}</a>
<div class="m-mediaModal__fullscreenButton" (click)="toggleFullscreen()">
<m-tooltip *ngIf="!isFullscreen" [anchor]="right" icon="fullscreen">Fullscreen</m-tooltip>
<m-tooltip *ngIf="isFullscreen" [anchor]="right" icon="fullscreen_exit">Exit fullscreen</m-tooltip>
<div class="m-mediaModal__theaterOverlay">
<a class="m-mediaModal__theaterOverlayTitle" [routerLink]="['/media', entity.entity_guid]">{{title}}</a>
<div class="m-mediaModal__fullscreenButton"
(click)="toggleFullscreen()"
(mouseenter)="hovering=true"
(mouseleave)="hovering=false"
[class.m-mediaModal__fullscreenButton--active]="hovering"
>
<i *ngIf="!isFullscreen" class="material-icons">fullscreen</i>
<i *ngIf="isFullscreen" class="material-icons">fullscreen_exit</i>
<!-- <m-tooltip *ngIf="!isFullscreen" [anchor]="right" icon="fullscreen">Fullscreen</m-tooltip>
<m-tooltip *ngIf="isFullscreen" [anchor]="right" icon="fullscreen_exit">Exit fullscreen</m-tooltip> -->
</div>
</div>
</div>
<!-- Media: video -->
<div class="m-mediaModal__stage m-mediaModal__stage--video" *ngIf="entity.modal_subtype === 'video'">
<!-- TODO OJM video stage with no full screen -->
<div class="m-mediaModal__theater m-mediaModal__theater--video" *ngIf="entity.modal_subtype === 'video'">
<!-- TODO OJM video theater with no full screen -->
</div>
</div>
<!-- </div> -->
<div class="m-mediaModal__contentWrapper">
<!-- Owner block -->
<div class="m-mediaModal__ownerBlock m-owner-block">
......@@ -53,10 +60,6 @@
</div>
<!-- Action buttons -->
<div class="m-mediaModal__actionButtonsWrapper">
<!-- <minds-button-boost *ngIf="entity.subtype != 'album'"
class="m-media-content--button-boost"
[object]="entity"
></minds-button-boost> -->
<div class="m-mediaModal__actionButtonsRow m-action-tabs">
<m-wire-button *ngIf="session.getLoggedInUser().guid != entity.owner_guid"
[object]="entity"
......
.minds-avatar-hovercard {
z-index: 9999999 !important;
z-index: 9999999;
}
.m-overlay-modal.m-overlayModal--media {
......@@ -19,44 +19,46 @@ m-media--modal {
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
border: 1px solid salmon;
max-height: 100%;
max-height: 98vh;
.m-mediaModal__theater {
border: 1px solid gold;
flex: 1 1 auto;
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 100%;
overflow: hidden; // .????
@include m-theme(){
background-color: rgba(themed($m-black-always), 0.95);
}
overflow: hidden; // .????
&:hover {
.m-mediaModal__stageOverlay{
.m-mediaModal__theaterOverlay{
opacity: 1;
}
}
.m-mediaModal__stage {
img{
max-width:100%;
height:auto;
img{
object-fit: contain;
flex: 1;
overflow: hidden;
}
m-video{
@include m-theme(){
background-color: themed($m-black-always);
}
m-video{
@include m-theme(){
background-color: themed($m-black-always);
}
video{
max-width:100%;
height:100%;
}
video{
max-width:100%;
height:100%;
}
}
.m-mediaModal__stageOverlay {
.m-mediaModal__theaterOverlay {
display: flex;
justify-content: space-between;
position: absolute;
......@@ -77,10 +79,11 @@ m-media--modal {
cursor: pointer;
}
.m-mediaModal__stageOverlayTitle {
.m-mediaModal__theaterOverlayTitle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-decoration: none;
@include m-theme(){
color: themed($m-white-always);
......@@ -96,9 +99,16 @@ m-media--modal {
@include m-theme(){
color: themed($m-white-always);
}
.m-tooltip--bubble {
right: 8px;
transition: transform 200ms linear;
// .m-tooltip--bubble {
// right: 8px;
// }
&.m-mediaModal__fullscreenButton--active {
transform: scale(1.2);
}
&:hover {
// TODO OJM: add --active class on hover and transform: scale(1.2)?
// i {
......@@ -113,13 +123,15 @@ m-media--modal {
// TODO OJM see .m-group--conversation
.m-mediaModal__contentWrapper {
border: 1px solid green;
display: flex;
flex-direction: column;
width: calc(33% - 12px); //1/3rd of screen
// width: 300px;
min-width: 300px;
// TODO OJM add a vh fallback
height: 96vh;
height: 100%; // TODO check this vh fallback
height: 90vh;
padding: 16px;
font-family: 'Roboto', Helvetica, sans-serif;
......@@ -268,6 +280,7 @@ m-media--modal {
.minds-avatar {
width: 36px;
height: 36px;
margin-left: 0;
}
.m-comment-attachment .item-image {
......
......@@ -17,8 +17,8 @@ export class MediaModalComponent implements OnInit, OnDestroy {
boosted: boolean;
inProgress: boolean = true;
isFullscreen: boolean = false;
isVideo: boolean = false;
navigatedAway: boolean = false;
hovering: boolean = false; // Used for fullscreen button transformation
routerSubscription: Subscription;
......@@ -38,13 +38,13 @@ export class MediaModalComponent implements OnInit, OnDestroy {
console.log(this.entity);
this.entity.thumbnail = `${this.minds.cdn_url}fs/v1/thumbnail/${this.entity.entity_guid}/xlarge`;
this.boosted = this.entity.boosted || this.entity.p2p_boosted;
this.isVideo = this.entity.subtype === 'video' ? true : false; // image || video
this.title = this.entity.message ? this.entity.message : `${this.entity.ownerObj.name}'s post`;
// Change the url to point to media page so user can easily share link
// (but don't actually redirect)
this.location.replaceState(`/media/${this.entity.entity_guid}`);
// Handle redirects
// When user clicks a link from inside the modal
this.routerSubscription = this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
......@@ -64,6 +64,46 @@ export class MediaModalComponent implements OnInit, OnDestroy {
});
}
// @HostListener('window:beforeunload', ['$event'])
// onWindowClose(event: any): void {
// // Do something
// event.preventDefault();
// event.returnValue = false;
// }
// Hijack browser refresh so it doesn't go to faux media page url
// @HostListener('window:beforeunload', ['$event'])
// onBeforeUnload(event) {
// console.log('***rfeeeresh');
// event.preventDefault();
// console.log('default prevented');
// // Fix browser history so browser refresh doesn't go to media page
// this.location.replaceState(this.entity.modal_source_url);
// // event.returnValue = ''; // Required for chrome
// // Then do the refresh
// // window.location.reload();
// // Prevent browser popup reload confirmation dialog
// return;
// }
// // TEMP
// hoverCheck(state: string) {
// console.log('*** ' + state);
// }
// TODO OJM is there a better way to get this?
getOwnerIconTime() {
const session = this.session.getLoggedInUser();
......@@ -98,7 +138,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
}
toggleFullscreen() {
const elem = document.querySelector('.m-mediaModal__stage');
const elem = document.querySelector('.m-mediaModal__theater');
// If fullscreen is not already enabled
if (!document['fullscreenElement'] && !document['webkitFullScreenElement'] && !document['mozFullScreenElement'] && !document['msFullscreenElement']) {
......
......@@ -148,7 +148,7 @@
</div>
<!-- Don't show comments for albums -->
<div class="mdl-grid m-media-content--comments mdl-color--white" *ngIf="canShowComments() && (activityService.allowComment$ | async)">
<div class="mdl-grid m-media-content--comments mdl-color--white" *ngIf="entity.guid && entity.subtype != 'album'">
<m-comments__tree
[entity]="entity"
>
......
import { ChangeDetectorRef, Component, OnInit, OnDestroy } from '@angular/core';
import { ChangeDetectorRef, Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
......@@ -10,23 +10,19 @@ import { RecommendedService } from '../components/video/recommended.service';
import { AttachmentService } from '../../../services/attachment';
import { ContextService } from '../../../services/context.service';
import { MindsTitle } from '../../../services/ux/title';
import { ActivityService } from '../../../common/services/activity.service';
@Component({
moduleId: module.id,
selector: 'm-media--view',
templateUrl: 'view.component.html',
providers: [
{
provide: RecommendedService,
useFactory: RecommendedService._,
deps: [Client]
},
ActivityService
],
providers: [{
provide: RecommendedService,
useFactory: RecommendedService._,
deps: [Client]
}],
})
export class MediaViewComponent implements OnInit, OnDestroy {
export class MediaViewComponent {
minds = window.Minds;
guid: string;
......@@ -36,12 +32,8 @@ export class MediaViewComponent implements OnInit, OnDestroy {
deleteToggle: boolean = false;
theaterMode: boolean = false;
allowComments = true;
menuOptions: Array<string> = ['edit', 'follow', 'feature',
'delete', 'report', 'set-explicit',
'subscribe', 'remove-explicit', 'rating',
'allow-comments', 'disable-comments'];
menuOptions: Array<string> = ['edit', 'follow', 'feature', 'delete', 'report', 'set-explicit', 'subscribe', 'remove-explicit', 'rating'];
paramsSubscription: Subscription;
queryParamsSubscription$: Subscription;
......@@ -55,8 +47,7 @@ export class MediaViewComponent implements OnInit, OnDestroy {
public route: ActivatedRoute,
public attachment: AttachmentService,
public context: ContextService,
private cd: ChangeDetectorRef,
protected activityService: ActivityService
private cd: ChangeDetectorRef
) { }
ngOnInit() {
......@@ -96,7 +87,7 @@ export class MediaViewComponent implements OnInit, OnDestroy {
}
if (response.entity) {
this.entity = response.entity;
this.allowComments = this.entity['allow_comments'];
switch (this.entity.subtype) {
case 'video':
this.context.set('object:video');
......@@ -169,14 +160,7 @@ export class MediaViewComponent implements OnInit, OnDestroy {
case 'remove-explicit':
this.setExplicit(false);
break;
case 'allow-comments':
this.entity.allow_comments = true;
this.activityService.toggleAllowComments(this.entity, true);
break;
case 'disable-comments':
this.entity.allow_comments = false;
this.activityService.toggleAllowComments(this.entity, false);
break;
}
}
......@@ -192,17 +176,6 @@ export class MediaViewComponent implements OnInit, OnDestroy {
});
}
canShowComments() {
if (!this.entity.guid) {
return false;
}
//Don't show comments on albums
if (this.entity.subtype === 'album') {
return false;
}
return (this.entity['comments:count'] >= 1);
}
private detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......
......@@ -22,11 +22,11 @@ export class FeaturesService {
if (typeof this._features[feature] === 'undefined') {
if (isDevMode() && !this._hasWarned(feature)) {
console.warn(`[FeaturedService] Feature '${feature}' is not declared. Assuming false.`);
console.warn(`[FeaturedService] Feature '${feature}' is not declared. Assuming true.`);
this._warnedCache[feature] = Date.now();
}
return false;
return true;
}
if (this._features[feature] === 'admin' && this.session.isAdmin()) {
......
......@@ -4,8 +4,8 @@ import { Injectable } from '@angular/core';
@Injectable()
export class TimeDiffService {
public source = interval(1000);
static _() {
return new TimeDiffService();
}
}
}
\ No newline at end of file
export let activityServiceMock = new function() {
this.toggleAllowComments = jasmine.createSpy('toggleAllowComponents').and.stub();
};
export let analyticsServiceMock = new function () {
this.send = jasmine.createSpy('send').and.stub();
this.onRouterInit = jasmine.createSpy('onRouterInit').and.stub();
this.onRouteChanged = jasmine.createSpy('onRouteChanged').and.stub();
this.preventDefault = jasmine.createSpy('preventDefault').and.stub();
this.wasDefaultPrevented = jasmine.createSpy('wasDefaultPrevented').and.stub();
};