...
 
Commits (13)
......@@ -11,6 +11,12 @@
flex-wrap: wrap;
}
&.m-topbar--navigation__centered {
max-width: 100%;
justify-content: center;
flex-wrap: wrap;
}
&:not(.m-topbar--navigation--text-only) .m-topbar--navigation--item span {
@media screen and (max-width: 840px) {
display: none;
......
<div class="m-toolbar">
<div class="m-topbar--row">
<div class="m-topbar--navigation m-topbar--navigation--text-only">
<div
class="m-topbar--navigation m-topbar--navigation__centered m-topbar--navigation--text-only"
>
<a
class="m-topbar--navigation--item"
routerLink="/analytics/admin"
......@@ -99,6 +101,13 @@
>
<span i18n="@@M__ADMIN_NAV__REPORTS">Reports</span>
</a>
<a
class="m-topbar--navigation--item"
routerLink="/admin/features"
routerLinkActive="m-topbar--navigation--item-active"
>
<span i18n="@@M__ADMIN_NAV__FEATURES">Features</span>
</a>
</div>
</div>
</div>
......@@ -122,3 +131,4 @@
<m-admin--reports-download
*ngIf="filter == 'reports-download'"
></m-admin--reports-download>
<m-admin--features *ngIf="filter == 'features'"></m-admin--features>
<div class="m-adminFeatures">
<ng-container *ngIf="!isLoading && !error">
<div class="m-adminFeatures--label" i18n>
<b>Environment</b>: {{ environment }}
</div>
<div class="m-adminFeatures--label" i18n>
<b>Features for</b>: {{ readableFor }}
</div>
<table class="m-adminFeatures--table" cellspacing="0" cellpadding="0">
<thead>
<tr>
<th class="m-adminFeaturesTable--cell__first">Feature</th>
<th *ngFor="let service of services">{{ service }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let feature of features">
<td class="m-adminFeaturesTable--cell__first">{{ feature.name }}</td>
<td
*ngFor="let service of services"
class="m-adminFeaturesTable--cell__value"
[class.m-adminFeaturesTable--cell__bestValue]="
isBestService(service, feature.services)
"
>
{{ labelForValue(feature.services[service]) }}
</td>
</tr>
</tbody>
</table>
</ng-container>
<ng-container *ngIf="isLoading">
<div class="m-adminFeatures--loader">
<div class="mdl-spinner mdl-js-spinner is-active" [mdl]></div>
</div>
</ng-container>
<ng-container *ngIf="error">
<div class="m-adminFeatures--error">
{{ error }}
</div>
</ng-container>
</div>
.m-adminFeatures {
max-width: 960px;
margin: 0 auto;
padding: 16px;
.m-adminFeatures--label {
margin-bottom: 8px;
padding: 0 4px;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
@include m-theme() {
color: themed($m-grey-400);
}
}
.m-adminFeatures--table {
width: 100%;
margin-top: 24px;
th,
td {
text-align: center;
&.m-adminFeaturesTable--cell__first {
text-align: left;
}
}
th {
padding: 4px;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 1px solid;
@include m-theme() {
color: themed($m-grey-400);
border-color: themed($m-black);
}
}
td {
padding: 8px 4px;
&.m-adminFeaturesTable--cell__value {
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
@include m-theme() {
color: themed($m-grey-400);
}
}
&.m-adminFeaturesTable--cell__bestValue {
font-weight: bold;
@include m-theme() {
text-shadow: 0 0 3px rgba(themed($m-blue), 0.6);
color: themed($m-black);
}
}
}
}
.m-adminFeatures--loader {
text-align: center;
margin: 64px 0;
}
.m-adminFeatures--error {
text-align: center;
margin: 100px 0;
font-size: 28px;
@include m-theme() {
color: themed($m-red);
}
}
}
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
} from '@angular/core';
import { Client } from '../../../services/api/client';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
type ServicesEntityStruc = {
[service: string]: boolean | null;
};
type ResponseFeaturesStruc = Array<{
name: string;
services: ServicesEntityStruc;
}>;
@Component({
selector: 'm-admin--features',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: 'admin-features.component.html',
})
export class AdminFeaturesComponent implements OnInit, OnDestroy {
isLoading: boolean;
for: string;
environment: string;
services: Array<string>;
features: ResponseFeaturesStruc;
error: string;
protected params$: Subscription;
constructor(
protected client: Client,
protected cd: ChangeDetectorRef,
protected route: ActivatedRoute
) {}
ngOnInit(): void {
this.params$ = this.route.params.subscribe(params => {
if (typeof params.for !== 'undefined') {
this.for = params.for;
this.load();
}
});
this.load();
}
ngOnDestroy(): void {
this.params$.unsubscribe();
}
async load(): Promise<void> {
this.isLoading = true;
this.error = '';
this.detectChanges();
try {
const response: any = await this.client.get('api/v2/admin/features', {
for: this.for || '',
});
this.environment = response.environment;
this.for = response.for;
this.services = response.services;
this.features = response.features;
} catch (e) {
this.error = (e && e.message) || 'Internal server error';
}
this.isLoading = false;
this.detectChanges();
}
get readableFor(): string {
if (!this.for) {
return 'Anonymous user';
}
return `@${this.for}`;
}
isBestService(
currentService: string,
services: ServicesEntityStruc
): boolean {
let bestService = this.services[0];
for (const service of this.services) {
if (services[service] !== null) {
bestService = service;
}
}
return currentService == bestService;
}
labelForValue(value: any): string {
if (value === false) {
return 'OFF';
} else if (value === null) {
return '\xa0';
} else if (!value) {
return '???';
}
return 'ON';
}
detectChanges(): void {
this.cd.markForCheck();
this.cd.detectChanges();
}
}
......@@ -14,6 +14,7 @@ import { AdminInteractions } from './controllers/admin/interactions/interactions
import { InteractionsTableComponent } from './controllers/admin/interactions/table/table.component';
import { AdminPurchasesComponent } from './controllers/admin/purchases/purchases.component';
import { AdminWithdrawals } from './controllers/admin/withdrawals/withdrawals.component';
import { AdminFeaturesComponent } from './controllers/admin/features/admin-features.component';
export const MINDS_DECLARATIONS: any[] = [
// Components
......@@ -35,4 +36,5 @@ export const MINDS_DECLARATIONS: any[] = [
AdminPurchasesComponent,
AdminWithdrawals,
AdminReportsDownload,
AdminFeaturesComponent,
];
......@@ -45,7 +45,7 @@
<h2>Experiments</h2>
<ul class="m-canaryExperiments__list">
<li>
Discovery algorithm by post age - 11th December '19
Server Side Rendering - 5th Feb 2020
</li>
</ul>
</div>
......
<ng-container *ngIf="error || channel; else loader">
<ng-container
*ngIf="
error || !proEnabled || !channel.pro_published || isOwner || isAdmin;
error ||
!proEnabled ||
!showPro ||
!channel.pro_published ||
isOwner ||
isAdmin;
else isProChannel
"
>
......
......@@ -28,6 +28,7 @@ export class ChannelContainerComponent implements OnInit, OnDestroy {
channel: MindsUser;
protected username: string;
protected showPro: boolean;
protected param$: Subscription;
......@@ -50,10 +51,11 @@ export class ChannelContainerComponent implements OnInit, OnDestroy {
this.param$ = this.route.params.subscribe(params => {
if (params['username']) {
this.username = params['username'];
this.showPro = !params['pro'] || params['pro'] !== '0';
if (
this.username &&
(!this.channel || this.channel.username != this.username)
(!this.channel || this.channel.username !== this.username)
) {
this.load();
}
......@@ -74,7 +76,7 @@ export class ChannelContainerComponent implements OnInit, OnDestroy {
}
async load() {
if (!this.username) {
if (!this.username || this.showPro === undefined) {
return;
}
......@@ -88,6 +90,7 @@ export class ChannelContainerComponent implements OnInit, OnDestroy {
this.channel = response.channel;
const shouldRedirectToProHandler =
this.showPro &&
!this.site.isProDomain &&
this.channel.pro_published &&
!this.isOwner &&
......
......@@ -2,7 +2,7 @@ import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule as NgCommonModule } from '@angular/common';
import { CommonModule } from '../../common/common.module';
import { Client } from '../../common/api/client.service';
import { Storage } from '../../services/storage';
import { CookieService } from '../../common/services/cookie.service';
import { ExperimentDirective } from './experiment.directive';
import { ExperimentsService } from './experiments.service';
......@@ -14,9 +14,9 @@ import { ExperimentsService } from './experiments.service';
providers: [
{
provide: ExperimentsService,
useFactory: (_client, _storage) =>
new ExperimentsService(_client, _storage),
deps: [Client, Storage],
useFactory: (_client, cookieService) =>
new ExperimentsService(_client, cookieService),
deps: [Client, CookieService],
},
],
})
......
......@@ -18,7 +18,7 @@
*ngIf="group && (group['is:member'] || group.membership == 2)"
>
<!-- Sidebar -->
<div class="m-groupGrid__left">
<div class="m-groupGrid__left" *mIfBrowser>
<m-videochat></m-videochat>
<header
......@@ -328,7 +328,7 @@
<div class="m-groupGrid__right" [hidden]="!showRight">
<!-- Conversation filter -->
<minds-groups-profile-conversation [group]="group">
<minds-groups-profile-conversation [group]="group" *mIfBrowser>
</minds-groups-profile-conversation>
<!-- END: Conversation filter -->
</div>
......
......@@ -183,8 +183,9 @@ export class GroupsProfile {
}
async load() {
if (isPlatformServer(this.platformId)) return;
this.resetMarkers();
if (isPlatformBrowser(this.platformId)) {
this.resetMarkers();
}
this.error = '';
this.group = null;
......@@ -199,38 +200,41 @@ export class GroupsProfile {
if (this.updateMarkersSubscription)
this.updateMarkersSubscription.unsubscribe();
this.updateMarkersSubscription = this.updateMarkers
.getByEntityGuid(this.guid)
.subscribe(
(marker => {
// this.updateMarkersSubscription = this.updateMarkers.markers.subscribe(markers => {
if (!marker) return;
this.group.hasGathering$ = interval(1000).pipe(
throttle(() => interval(2000)), //only allow once per 2 seconds
startWith(0),
map(
() =>
[marker].filter(
marker =>
marker.entity_guid == this.group.guid &&
marker.marker == 'gathering-heartbeat' &&
marker.updated_timestamp > Date.now() / 1000 - 60 //1 minute tollerance
).length > 0
)
);
let hasMarker =
marker.read_timestamp < marker.updated_timestamp &&
marker.entity_guid == this.group.guid &&
marker.marker != 'gathering-heartbeat';
if (hasMarker) this.resetMarkers();
}).bind(this)
);
// Check for comment updates
this.joinCommentsSocketRoom();
if (isPlatformBrowser(this.platformId)) {
this.updateMarkersSubscription = this.updateMarkers
.getByEntityGuid(this.guid)
.subscribe(
(marker => {
// this.updateMarkersSubscription = this.updateMarkers.markers.subscribe(markers => {
if (!marker) return;
this.group.hasGathering$ = interval(1000).pipe(
throttle(() => interval(2000)), //only allow once per 2 seconds
startWith(0),
map(
() =>
[marker].filter(
marker =>
marker.entity_guid == this.group.guid &&
marker.marker == 'gathering-heartbeat' &&
marker.updated_timestamp > Date.now() / 1000 - 60 //1 minute tollerance
).length > 0
)
);
let hasMarker =
marker.read_timestamp < marker.updated_timestamp &&
marker.entity_guid == this.group.guid &&
marker.marker != 'gathering-heartbeat';
if (hasMarker) this.resetMarkers();
}).bind(this)
);
// Check for comment updates
this.joinCommentsSocketRoom();
}
this.updateMeta();
this.context.set('activity', {
......
......@@ -37,6 +37,12 @@
</div>
<div class="m-proChannelFooter__items m-proChannelFooter__socialItems">
<a [routerLink]="['/' + user.username + '/feed', { pro: '0' }]">
<img
alt="Minds"
src="https://cdn-assets.minds.com/front/dist/en/assets/logos/bulb.svg"
/>
</a>
<span *ngFor="let profile of footerSocialProfiles">
<a
*ngIf="profile.key && profile.value"
......
......@@ -3,10 +3,17 @@
margin-bottom: 24px;
color: var(--m-pro--text-color);
.m-proChannelFooter__items > * {
display: inline-block;
margin: 8px 16px;
vertical-align: middle;
.m-proChannelFooter__items {
> * {
display: inline-block;
margin: 8px 16px;
vertical-align: middle;
}
img {
height: 24px;
filter: grayscale(100%);
}
}
.m-proChannelFooter__socialItems {
......@@ -14,9 +21,13 @@
font-size: 1.5em;
}
.m-proChannelFooter__text {
text-transform: uppercase;
letter-spacing: 0.1em;
.m-proChannelFooter__static {
margin-bottom: 16px;
.m-proChannelFooter__text {
text-transform: uppercase;
letter-spacing: 0.1em;
}
}
a {
......
......@@ -21,7 +21,7 @@ export class SocketsService {
subscriptions: any = {};
rooms: string[] = [];
debug: boolean = false;
public error$: BehaviorSubject<boolean>;
public error$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
constructor(
public session: Session,
......@@ -71,9 +71,7 @@ export class SocketsService {
setUpDefaultListeners() {
this.socket.on('connect', () => {
this.error$
? this.error$.next(false)
: (this.error$ = new BehaviorSubject<boolean>(false));
this.error$.next(false);
this.nz.run(() => {
if (this.debug)
console.log(`[ws]::connected to ${this.SOCKET_IO_SERVER}`);
......
src/assets/email-2020/confirmation-splash.jpg

26.6 KB

src/assets/email-2020/dl-android-app.png

4.88 KB

src/assets/email-2020/dl-ios-app.png

5.69 KB

This diff is collapsed.
src/assets/email-2020/sep.png

157 Bytes

src/assets/email-2020/verify-account-btn.png

4.62 KB