Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • omadrid/front
  • minds/front
  • joe59/front
  • markharding/front
  • eiennohi/front
  • edgebal/front
  • msantang78/front
  • bhayward93/front
  • xorgy/front
  • duyquoc/front
  • benhayward.ben/front
  • mnurzia/front
  • priestd09/front
  • dknunn/front
  • Yersinia/front
  • literalpie/front
  • maruthi-adithya/front
  • javanick/front
  • juanmsolaro/front
  • ascenderking/front
  • fabiolalombardim/front
  • jim-toth/front
  • Shivathanu/front
  • pestixaba/front
  • project_connection/front
  • mul53/front
  • iamonuwa/front
  • manishoo/front
  • namesty/front
  • AaronTheBruce/front
  • bedriguler/front
  • th2tran/front
  • jun784/front
  • mdstevens044/front
  • CodingNagger/front
  • heenachauhan201/front
  • diazairic/front
  • m994/front
  • webprodev/minds_front
  • chaoukiammar/front
  • benn7/front
  • ung1807/front
  • vinliao/front-patch-1
  • suhailkakar/front
  • theokeist/minds-blog
45 results
Select Git revision
Show changes
Commits on Source (78)
Showing
with 5578 additions and 2432 deletions
image: markharding/minds-front-base
image: minds/ci-front:latest
stages:
- test
......@@ -17,7 +17,7 @@ variables:
CYPRESS_CACHE_FOLDER: '$CI_PROJECT_DIR/cache/Cypress'
test:
image: circleci/node:8-browsers
image: circleci/node:13-browsers
stage: test
script:
- npm ci
......@@ -72,6 +72,8 @@ qa:manual:
only:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
allow_failure: false
......@@ -86,8 +88,10 @@ build:review:
script:
- npm ci && npm install -g gulp-cli
- npm run postinstall
- gulp build.sass && gulp build.sass ##weird build needs to be run twice for now
- gulp build.sass --deploy-url=/en && gulp build.sass --deploy-url=/en ##weird build needs to be run twice for now
- sh build/base-locale.sh dist
- ng run minds:server:production
- npm run compile:server
artifacts:
name: '$CI_COMMIT_REF_SLUG'
paths:
......@@ -95,6 +99,8 @@ build:review:
except:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
build:production:en:
......@@ -106,13 +112,19 @@ build:production:en:
- npm run postinstall
- gulp build.sass --deploy-url=https://cdn-assets.minds.com/front/dist/en && gulp build.sass --deploy-url=https://cdn-assets.minds.com/front/dist/en ##weird build needs to be run twice for now
- sh build/base-locale.sh dist https://cdn-assets.minds.com/front/dist
- ng run minds:server:production
- npm run compile:server
artifacts:
name: '$CI_COMMIT_REF_SLUG'
paths:
- dist/en
- dist/server.js
- dist/server
only:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
build:production:i18n:
......@@ -131,6 +143,8 @@ build:production:i18n:
only:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
#################
......@@ -155,13 +169,15 @@ prepare:review:
- docker:dind
script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker build -t $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID -f containers/front-init/Dockerfile dist/.
- docker push $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID
- docker build -t $CI_REGISTRY_IMAGE/server:$CI_PIPELINE_ID -f containers/server/Dockerfile dist/.
- docker push $CI_REGISTRY_IMAGE/server:$CI_PIPELINE_ID
dependencies:
- build:review
except:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
prepare:review:sentry:
......@@ -171,6 +187,8 @@ prepare:review:sentry:
except:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
dependencies:
- build:review
......@@ -182,11 +200,13 @@ prepare:production:
- docker:dind
script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker build -t $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID -f containers/front-init/Dockerfile dist/.
- docker push $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID
- docker build -t $CI_REGISTRY_IMAGE/server:$CI_PIPELINE_ID -f containers/server/Dockerfile dist/.
- docker push $CI_REGISTRY_IMAGE/server:$CI_PIPELINE_ID
only:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
dependencies:
- build:production:en
......@@ -199,6 +219,8 @@ prepare:production:sentry:
only:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
dependencies:
- build:production:en
......@@ -222,6 +244,8 @@ prepare:production:sentry:
except:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
review:start:
......@@ -233,8 +257,8 @@ review:start:
- "helm upgrade \
--install \
--reuse-values \
--set frontInit.image.repository=$CI_REGISTRY_IMAGE/front-init \
--set-string frontInit.image.tag=$CI_PIPELINE_ID \
--set front.image.repository=$CI_REGISTRY_IMAGE/server \
--set-string front.image.tag=$CI_PIPELINE_ID \
--set domain=$CI_BUILD_REF_SLUG.$KUBE_INGRESS_BASE_DOMAIN \
--set elasticsearch.clusterName=$CI_BUILD_REF_SLUG--elasticsearch \
--wait \
......@@ -249,6 +273,8 @@ review:start:
except:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
review:stop:
......@@ -268,10 +294,10 @@ review:stop:
## Sync assets with CDN
- aws s3 sync dist $S3_REPOSITORY_URL
- $(aws ecr get-login --no-include-email --region us-east-1)
## Update docker front-init container
## Update docker server container
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker pull $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID
- docker tag $CI_REGISTRY_IMAGE/front-init:$CI_PIPELINE_ID $ECR_REPOSITORY_URL:$IMAGE_LABEL
- docker pull $CI_REGISTRY_IMAGE/server:$CI_PIPELINE_ID
- docker tag $CI_REGISTRY_IMAGE/server:$CI_PIPELINE_ID $ECR_REPOSITORY_URL:$IMAGE_LABEL
- docker push $ECR_REPOSITORY_URL:$IMAGE_LABEL
## Deploy the new container in rolling restart
- aws ecs update-service --service=$ECS_SERVICE --force-new-deployment --region us-east-1 --cluster=$ECS_CLUSTER
......@@ -280,10 +306,6 @@ review:stop:
dependencies:
- build:production:en
- build:production:i18n
only:
refs:
- master
- test/gitlab-ci
staging:fpm:
<<: *deploy
......@@ -294,6 +316,28 @@ staging:fpm:
environment:
name: staging
url: https://www.minds.com # requires staging cookie
only:
refs:
- master
- epic/SSR
- test/gitlab-ci
review:preprod:
<<: *deploy
stage: review
when: manual
allow_failure: true
variables:
IMAGE_LABEL: 'preprod'
ECS_SERVICE: $ECS_APP_PREPROD_SERVICE
environment:
name: preprod
url: https://www.minds.com # requires preprod cookie
# dependencies:
# - build:review
# except:
# refs:
# - master
deploy:canary:
<<: *deploy
......@@ -306,6 +350,11 @@ deploy:canary:
url: https://www.minds.com/?canary=1 # requires canary cookie
when: manual
allow_failure: false # prevents auto deploy to full production
only:
refs:
- master
- epic/SSR
- test/gitlab-ci
deploy:production:
<<: *deploy
......@@ -318,6 +367,11 @@ deploy:production:
url: https://www.minds.com
when: delayed
start_in: 12 hours # reduce? can always be deployed manually earlier too
only:
refs:
- master
- epic/SSR
- test/gitlab-ci
#################
# Cleanup stage #
......@@ -330,4 +384,6 @@ cleanup:review: # We stop the review site after the e2e tests have run
except:
refs:
- master
- epic/SSR
- production
- test/gitlab-ci
......@@ -3,7 +3,7 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"v5.x": {
"minds": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
......@@ -12,10 +12,11 @@
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.php",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"extractCss": true,
"assets": ["src/assets", "src/favicon.ico"],
"styles": [
"node_modules/material-design-lite/dist/material.blue_grey-amber.min.css",
......@@ -64,22 +65,22 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "v5.x:build"
"browserTarget": "minds:build"
},
"configurations": {
"production": {
"browserTarget": "v5.x:build:production"
"browserTarget": "minds:build:production"
},
"hmr": {
"hmr": true,
"browserTarget": "v5.x:build:hmr"
"browserTarget": "minds:build:hmr"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "v5.x:build"
"browserTarget": "minds:build"
}
},
"test": {
......@@ -102,10 +103,28 @@
"tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/server",
"main": "src/main.server.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
}
}
},
"v5.x-e2e": {
"minds-e2e": {
"root": "e2e",
"sourceRoot": "e2e",
"projectType": "application",
......@@ -114,7 +133,7 @@
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "v5.x:serve"
"devServerTarget": "minds:serve"
}
},
"lint": {
......@@ -127,7 +146,7 @@
}
}
},
"defaultProject": "v5.x",
"defaultProject": "minds",
"schematics": {
"@schematics/angular:component": {
"prefix": "m",
......
#!/bin/sh
export NODE_OPTIONS="--max-old-space-size=3584"
ng build --prod --vendor-chunk --output-path="$1/en/" --deploy-url="$2/en/" --build-optimizer=false
ng build --prod --vendor-chunk --output-path="$1/en/" --deploy-url="$2/en/" --build-optimizer=true --stats-json
FROM alpine:edge
RUN apk add --no-cache \
chromium \
git \
build-base \
libstdc++ \
make \
g++ \
python \
curl \
udev \
nodejs=8.9.1-r0 \
libsass
RUN npm install -g typescript gulp @angular/cli
ENV CHROME_BIN=/usr/bin/chromium-browser
\ No newline at end of file
FROM node:13-alpine
RUN apk add --no-cache \
git \
libsass
RUN npm install -g typescript @angular/cli
# Gitlab CI has limited memory
ENV NODE_OPTIONS="--max-old-space-size=1024"
\ No newline at end of file
###
# Before running this container, ensure the server is built
###
FROM node:13-alpine
COPY . /dist
CMD node /dist/server
VOLUME ["/dist"]
\ No newline at end of file
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "v5.x",
"name": "minds",
"version": "0.0.0",
"license": "MIT",
"scripts": {
......@@ -7,38 +7,53 @@
"start": "ng serve",
"preinstall": "git config core.hooksPath .git/hooks/",
"prebuild": "gulp build.sass",
"build": "ng build --prod",
"prebuild-dev": "gulp build.sass --deploy-url=http://localhost/en",
"build-dev": "ng build --output-path dist/en --deploy-url=/en/ --watch=true --poll=800",
"serve-dev": "ng serve --host=0.0.0.0 --deploy-url=/en/ --configuration=hmr --hmr --poll=800 --progress",
"build": "sh build/base-locale.sh dist",
"prebuild-dev": "gulp build.sass --deploy-url=http://localhost",
"build-dev": "npm run build:dev",
"serve-dev": "npm run server:ssr",
"build:dev": "ng build --output-path dist --deploy-url=/ --watch=true --poll=800",
"serve:dev": "ng serve --host=0.0.0.0 --deploy-url=/ --configuration=hmr --hmr --poll=800 --progress --disableHostCheck=true",
"test": "ng test",
"lint": "ng lint",
"e2e": "cypress run --debug",
"e2e-open": "cypress open",
"postinstall": "node patch.js"
"postinstall": "node patch.js",
"compile:server": "webpack --config webpack.server.config.js --progress --colors",
"serve:ssr": "node dist/server",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"build:client-and-server-bundles": "npm run build && ng run minds:server:production",
"bundle-report": "webpack-bundle-analyzer dist/en/stats.json"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.0.3",
"@angular/cdk": "^8.2.3",
"@angular/common": "~8.0.3",
"@angular/compiler": "~8.0.3",
"@angular/core": "~8.0.3",
"@angular/forms": "~8.0.3",
"@angular/animations": "~8.1.1",
"@angular/common": "~8.1.1",
"@angular/compiler": "~8.1.1",
"@angular/core": "~8.1.1",
"@angular/forms": "~8.1.1",
"@angular/http": "~7.2.0",
"@angular/platform-browser": "~8.0.3",
"@angular/platform-browser-dynamic": "~8.0.3",
"@angular/router": "~8.0.3",
"@angular/platform-browser": "~8.1.1",
"@angular/platform-browser-dynamic": "~8.1.1",
"@angular/platform-server": "~8.1.1",
"@angular/router": "~8.1.1",
"@gorniv/ngx-universal": "^2.0.1",
"@nguniversal/common": "~8.1.1",
"@nguniversal/express-engine": "^8.1.1",
"@nguniversal/module-map-ngfactory-loader": "~8.1.1",
"@nguniversal/socket-engine": "~8.1.1",
"@angular/cdk": "^8.2.3",
"@sentry/browser": "^5.11.1",
"angular-plotly.js": "^1.3.2",
"bn.js": "^4.11.8",
"braintree-web": "3.41.0",
"cookie-parser": "^1.4.4",
"core-js": "~2.6.2",
"dexie": "^2.0.4",
"ethjs": "~0.4.0",
"ethjs-account": "^0.1.4",
"ethjs-provider-signer": "^0.1.4",
"ethjs-signer": "0.1.1",
"express-http-proxy": "^1.6.0",
"global": "^4.3.2",
"material-design-icons": "~3.0.1",
"material-design-lite": "~1.3.0",
......@@ -47,6 +62,7 @@
"ng-pick-datetime": "^7.0.0",
"ngx-drag-drop": "^2.0.0",
"ngx-plyr": "^3.0.1",
"node-cache": "^5.1.0",
"plotly.js": "^1.47.4",
"plyr": "^3.5.6",
"qrcodejs2": "0.0.2",
......@@ -54,14 +70,15 @@
"socket.io-client": "^2.2.0",
"textarea-caret": "^3.1.0",
"tslib": "~1.9.3",
"webpack-node-externals": "^1.7.2",
"webtorrent": "^0.103.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.13.9",
"@angular/cli": "^7.2.1",
"@angular/compiler-cli": "~8.0.3",
"@angular/language-service": "~8.0.3",
"@angular-devkit/build-angular": "0.803.21",
"@angular/cli": "^8.1.1",
"@angular/compiler-cli": "~8.1.1",
"@angular/language-service": "~8.1.1",
"@angularclass/hmr": "^2.1.3",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.4",
......@@ -91,7 +108,9 @@
"protractor": "~5.4.2",
"ts-node": "~7.0.0",
"tslint": "~5.12.0",
"typescript": "~3.4.5"
"typescript": "~3.4.5",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10"
},
"husky": {
"hooks": {
......
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { join } from 'path';
import { readFileSync } from 'fs';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
import { enableProdMode } from '@angular/core';
import { XhrFactory } from '@angular/common/http';
import { NgxRequest, NgxResponce } from '@gorniv/ngx-universal';
import * as express from 'express';
import * as compression from 'compression';
import * as cookieparser from 'cookie-parser';
import isMobileOrTablet from './src/app/helpers/is-mobile-or-tablet';
const domino = require('domino');
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
// gzip
app.use(compression());
// cokies
app.use(cookieparser());
const PORT = process.env.PORT || 4200;
const DIST_FOLDER = join(process.cwd(), 'dist/en');
const template = readFileSync(join(DIST_FOLDER, 'index.html')).toString();
const win = domino.createWindow(template);
global['window'] = win;
global['Node'] = win.Node;
global['navigator'] = win.navigator;
global['screen'] = { width: 0, height: 0 };
global['Event'] = win.Event;
global['Event']['prototype'] = win.Event.prototype;
global['KeyboardEvent'] = global['Event'];
global['document'] = win.document;
global['window']['Promise'] = global.Promise;
global['window']['localStorage'] = {
getItem: () => null,
setItem: () => {},
removeItem: () => {},
};
global['localStorage'] = global['window']['localStorage'];
global['window']['scrollTo'] = pos => {};
Object.defineProperty(window.document, 'cookie', {
writable: true,
value: 'myCookie=omnomnom',
});
Object.defineProperty(window.document, 'referrer', {
writable: true,
value: '',
});
Object.defineProperty(window.document, 'localStorage', {
writable: true,
value: global['window']['localStorage'],
});
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {
AppServerModuleNgFactory,
LAZY_MODULE_MAP,
} = require('./dist/server/main');
const {
provideModuleMap,
} = require('@nguniversal/module-map-ngfactory-loader');
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
})
);
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
// Server static files from dist folder
app.get('*.*', express.static(DIST_FOLDER));
// Socket.io hitting wrong endpoint (dev?)
app.get('/socket.io', (req, res) => {
res.send('You are using the wrong domain.');
});
// /undefined is an issue with angular
app.get('/undefined', (req, res) => {
res.send('There was problem');
});
// cache
const NodeCache = require('node-cache');
const myCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 120 });
const cache = () => {
return (req, res, next) => {
const sessKey =
Object.entries(req.cookies)
.filter(kv => kv[0] !== 'mwa' && kv[0] !== 'XSRF-TOKEN')
.join(':') || 'loggedout';
const key =
`__express__/${sessKey}/` +
(req.originalUrl || req.url) +
(isMobileOrTablet() ? '/mobile' : '/desktop');
const exists = myCache.has(key);
if (exists) {
console.log(`from cache: ${key}`);
const cachedBody = myCache.get(key);
res.send(cachedBody);
return;
} else {
res.sendResponse = res.send;
res.send = body => {
myCache.set(key, body);
res.sendResponse(body);
};
next();
}
};
};
// All regular routes use the Universal engine
app.get('*', cache(), (req, res) => {
const http =
req.headers['x-forwarded-proto'] === undefined
? 'http'
: req.headers['x-forwarded-proto'];
const url = req.originalUrl;
// tslint:disable-next-line:no-console
console.time(`GET: ${url}`);
res.render(
'index',
{
req: req,
res: res,
// provers from server
providers: [
// for http and cookies
{
provide: REQUEST,
useValue: req,
},
{
provide: RESPONSE,
useValue: res,
},
// for cookie
{
provide: NgxRequest,
useValue: req,
},
{
provide: NgxResponce,
useValue: res,
},
// for absolute path
{
provide: 'ORIGIN_URL',
useValue: `${http}://${req.headers.host}`,
},
],
},
(err, html) => {
if (!!err) {
throw err;
}
// tslint:disable-next-line:no-console
console.timeEnd(`GET: ${url}`);
res.send(html);
}
);
});
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});
import { NgModule } from '@angular/core';
import { MindsModule } from './app.module';
import { Minds } from './app.component';
import * as PlotlyJS from 'plotly.js/dist/plotly-basic.min.js';
import { PlotlyModule } from 'angular-plotly.js';
import { CookieModule } from '@gorniv/ngx-universal';
PlotlyModule.plotlyjs = PlotlyJS;
@NgModule({
imports: [MindsModule, PlotlyModule, CookieModule],
bootstrap: [Minds],
providers: [{ provide: 'ORIGIN_URL', useValue: location.origin }],
})
export class AppBrowserModule {}
......@@ -40,11 +40,16 @@
</ng-container>
<m-body
[class.has-markers-sidebar]="
session.isLoggedIn() &&
!isProDomain &&
!featuresService.has('navigation-2020')
"
[class.has-v2-navbar]="featuresService.has('top-feeds')"
[class.has-v3-navbar]="featuresService.has('navigation-2020')"
[class.is-pro-domain]="isProDomain"
>
<m-emailConfirmation></m-emailConfirmation>
<m-emailConfirmation *ngIf="!isProDomain"></m-emailConfirmation>
<m-announcement [id]="'festival:sale'" *mIfFeature="'radiocity'">
<span
class="m-blockchain--wallet-address-notice--action"
......@@ -66,16 +71,23 @@
</ng-template>
</m-body>
<m-messenger *ngIf="minds.LoggedIn && !isProDomain"></m-messenger>
<ng-container *mIfBrowser>
<m-messenger *ngIf="session.isLoggedIn() && !isProDomain"></m-messenger>
</ng-container>
<m-hovercard-popup></m-hovercard-popup>
<m-overlay-modal></m-overlay-modal>
<m--blockchain--transaction-overlay></m--blockchain--transaction-overlay>
<m-modal--tos-updated *ngIf="session.isLoggedIn()"></m-modal--tos-updated>
<m-juryDutySession__summons
*ngIf="session.isLoggedIn() && !isProDomain"
></m-juryDutySession__summons>
<ng-container *mIfBrowser>
<m-juryDutySession__summons
*ngIf="session.isLoggedIn() && !isProDomain"
></m-juryDutySession__summons>
</ng-container>
<m-modal-signup
*ngIf="!isProDomain && !session.getLoggedInUser()"
......
@import 'defaults';
@import 'themes';
@import 'foundation/grid-values.scss';
html,
body {
......@@ -38,6 +39,15 @@ m-app {
grid-gap: 0;
height: 100%;
@media screen and (max-width: $m-grid-max-tablet) {
grid-template-columns: 71px 5fr 4fr;
}
@media screen and(max-width: $m-grid-max-mobile) {
display: flex;
flex-direction: column;
}
}
}
......
import {
ChangeDetectorRef,
Component,
PLATFORM_ID,
Inject,
HostBinding,
OnDestroy,
OnInit,
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { NotificationService } from './modules/notifications/notification.service';
import { AnalyticsService } from './services/analytics';
......@@ -16,7 +19,7 @@ import { ContextService } from './services/context.service';
import { Web3WalletService } from './modules/blockchain/web3-wallet.service';
import { Client } from './services/api/client';
import { WebtorrentService } from './modules/webtorrent/webtorrent.service';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute, NavigationEnd, Router, Route } from '@angular/router';
import { ChannelOnboardingService } from './modules/onboarding/channel/onboarding.service';
import { BlockListService } from './common/services/block-list.service';
import { FeaturesService } from './services/features.service';
......@@ -28,17 +31,17 @@ import { SsoService } from './common/services/sso.service';
import { Subscription } from 'rxjs';
import { RouterHistoryService } from './common/services/router-history.service';
import { PRO_DOMAIN_ROUTES } from './modules/pro/pro.module';
import { ConfigsService } from './common/services/configs.service';
import { MetaService } from './common/services/meta.service';
import { filter, map, mergeMap, first } from 'rxjs/operators';
@Component({
moduleId: module.id,
selector: 'm-app',
templateUrl: 'app.component.html',
})
export class Minds implements OnInit, OnDestroy {
name: string;
minds = window.Minds;
ready: boolean = false;
showOnboarding: boolean = false;
......@@ -49,6 +52,8 @@ export class Minds implements OnInit, OnDestroy {
protected router$: Subscription;
protected routerConfig: Route[];
constructor(
public session: Session,
public route: ActivatedRoute,
......@@ -67,11 +72,15 @@ export class Minds implements OnInit, OnDestroy {
public featuresService: FeaturesService,
public themeService: ThemeService,
private bannedService: BannedService,
@Inject(PLATFORM_ID) private platformId: Object,
private diagnostics: DiagnosticsService,
private routerHistoryService: RouterHistoryService,
private site: SiteService,
private sso: SsoService,
private cd: ChangeDetectorRef
private metaService: MetaService,
private configs: ConfigsService,
private cd: ChangeDetectorRef,
private socketsService: SocketsService
) {
this.name = 'Minds';
......@@ -82,8 +91,27 @@ export class Minds implements OnInit, OnDestroy {
async ngOnInit() {
this.useNewNavigation = this.featuresService.has('navigation-2020');
// MH: does loading meta tags before the configs have been set cause issues?
this.router$ = this.router.events
.pipe(
filter(e => e instanceof NavigationEnd),
map(() => this.route),
map(route => {
while (route.firstChild) route = route.firstChild;
return route;
}),
// filter(route => route.outlet === 'primary')
mergeMap(route => route.data)
)
.subscribe(data => {
this.metaService.reset(data);
});
try {
this.diagnostics.setUser(this.minds.user);
this.updateMeta(); // Because the router is setup before our configs
// Setup sentry/diagnostic configs
this.diagnostics.setUser(this.configs.get('user'));
this.diagnostics.listen(); // Listen for user changes
if (this.sso.isRequired()) {
......@@ -106,7 +134,9 @@ export class Minds implements OnInit, OnDestroy {
async initialize() {
this.blockListService.fetch();
if (!this.site.isProDomain) {
if (this.site.isProDomain) {
this.site.listen();
} else {
this.notificationService.getNotifications();
}
......@@ -116,16 +146,12 @@ export class Minds implements OnInit, OnDestroy {
this.showOnboarding = await this.onboardingService.showModal();
}
if (this.minds.user.language !== this.minds.language) {
console.log(
'[app]:: language change',
this.minds.user.language,
this.minds.language
);
const user = this.session.getLoggedInUser();
const language = this.configs.get('language');
setTimeout(() => {
window.location.reload(true);
});
if (user.language !== language) {
console.log('[app]:: language change', user.language, language);
window.location.reload(true);
}
}
});
......@@ -157,11 +183,14 @@ export class Minds implements OnInit, OnDestroy {
this.webtorrent.setUp();
this.themeService.setUp();
this.socketsService.setUp();
}
ngOnDestroy() {
this.loginReferrer.unlisten();
this.scrollToTop.unlisten();
this.router$.unsubscribe();
}
@HostBinding('class') get cssColorSchemeOverride() {
......@@ -176,6 +205,12 @@ export class Minds implements OnInit, OnDestroy {
return this.site.isProDomain;
}
private updateMeta(): void {
let route = this.route;
while (route.firstChild) route = route.firstChild;
this.metaService.reset(route.snapshot.data);
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
......
......@@ -3,13 +3,18 @@ import {
NgModule,
Injectable,
ErrorHandler,
APP_INITIALIZER,
APP_BOOTSTRAP_LISTENER,
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {
BrowserModule,
BrowserTransferStateModule,
} from '@angular/platform-browser';
// import { TransferHttpCacheModule } from '@nguniversal/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { CaptchaModule } from './modules/captcha/captcha.module';
import { environment } from '../environments/environment';
import { Minds } from './app.component';
......@@ -72,14 +77,10 @@ import { ChannelContainerModule } from './modules/channel-container/channel-cont
import { UpgradesModule } from './modules/upgrades/upgrades.module';
import * as Sentry from '@sentry/browser';
import { CookieModule } from '@gorniv/ngx-universal';
import { HomepageModule } from './modules/homepage/homepage.module';
import { OnboardingV2Module } from './modules/onboarding-v2/onboarding.module';
Sentry.init({
dsn: 'https://3f786f8407e042db9053434a3ab527a2@sentry.io/1538008', // TODO: do not hardcard
release: environment.version,
environment: (<any>window.Minds).environment || 'development',
});
import { ConfigsService } from './common/services/configs.service';
@Injectable()
export class SentryErrorHandler implements ErrorHandler {
......@@ -100,12 +101,18 @@ export class SentryErrorHandler implements ErrorHandler {
MINDS_PLUGIN_DECLARATIONS,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
BrowserModule.withServerTransition({ appId: 'm-app' }),
BrowserTransferStateModule,
CookieModule.forRoot(),
// TransferHttpCacheModule,
//BrowserAnimationsModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot(MindsAppRoutes, { onSameUrlNavigation: 'reload' }),
RouterModule.forRoot(MindsAppRoutes, {
// initialNavigation: 'enabled',
onSameUrlNavigation: 'reload',
}),
CaptchaModule,
CommonModule,
ProModule, // NOTE: Pro Module should be declared _BEFORE_ anything else
......@@ -151,6 +158,7 @@ export class SentryErrorHandler implements ErrorHandler {
CanaryModule,
ChannelsModule,
UpgradesModule,
//PlotlyModule,
//last due to :username route
ChannelContainerModule,
......@@ -160,6 +168,12 @@ export class SentryErrorHandler implements ErrorHandler {
MindsAppRoutingProviders,
MINDS_PROVIDERS,
MINDS_PLUGIN_PROVIDERS,
{
provide: APP_INITIALIZER,
useFactory: configs => () => configs.loadFromRemote(),
deps: [ConfigsService],
multi: true,
},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
......
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { ServerTransferStateModule } from '@angular/platform-server';
import { XhrFactory } from '@angular/common/http';
import * as xhr2 from 'xhr2';
import { MindsModule } from './app.module';
import { Minds } from './app.component';
import { PlotlyModule } from 'angular-plotly.js';
import { CookieService, CookieBackendService } from '@gorniv/ngx-universal';
PlotlyModule.plotlyjs = {
plot: () => {
// This simply satisfies the isValid() error
},
};
// activate cookie for server-side rendering
export class ServerXhr implements XhrFactory {
build(): XMLHttpRequest {
xhr2.prototype._restrictedHeaders.cookie = false;
return new xhr2.XMLHttpRequest();
}
}
@NgModule({
imports: [
MindsModule,
ServerModule,
ModuleMapLoaderModule,
ServerTransferStateModule,
PlotlyModule,
],
providers: [
{ provide: XhrFactory, useClass: ServerXhr },
{
provide: CookieService,
useClass: CookieBackendService,
},
],
bootstrap: [Minds],
})
export class AppServerModule {}
import { Cookie } from '../../services/cookie';
import { CookieService } from '../services/cookie.service';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { environment } from '../../../environments/environment';
......@@ -7,13 +7,12 @@ import { environment } from '../../../environments/environment';
*/
export class MindsHttpClient {
base: string = '/';
cookie: Cookie = new Cookie();
static _(http: HttpClient) {
return new MindsHttpClient(http);
static _(http: HttpClient, cookie: CookieService) {
return new MindsHttpClient(http, cookie);
}
constructor(public http: HttpClient) {}
constructor(public http: HttpClient, private cookie: CookieService) {}
/**
* Return a GET request
......
import { NgModule } from '@angular/core';
import { CommonModule as NgCommonModule } from '@angular/common';
import { NgModule, inject } from '@angular/core';
import {
CommonModule as NgCommonModule,
isPlatformServer,
Location,
} from '@angular/common';
import { RouterModule, Router, Routes } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
......@@ -60,6 +64,7 @@ import { InlineEditorComponent } from './components/editors/inline-editor.compon
import { AttachmentService } from '../services/attachment';
import { MaterialBoundSwitchComponent } from './components/material/bound-switch.component';
import { IfFeatureDirective } from './directives/if-feature.directive';
import { IfBrowserDirective } from './directives/if-browser.directive';
import { MindsEmoji } from './components/emoji/emoji';
import { CategoriesSelectorComponent } from './components/categories/selector/selector.component';
import { CategoriesSelectedComponent } from './components/categories/selected/selected.component';
......@@ -115,7 +120,7 @@ import { ToggleComponent } from './components/toggle/toggle.component';
import { MarketingAsFeaturedInComponent } from './components/marketing/as-featured-in.component';
import { SidebarMenuComponent } from './components/sidebar-menu/sidebar-menu.component';
import { ChartV2Component } from './components/chart-v2/chart-v2.component';
import * as PlotlyJS from 'plotly.js/dist/plotly.js';
//import * as PlotlyJS from 'plotly.js/dist/plotly.js';
import { PlotlyModule } from 'angular-plotly.js';
import { PageLayoutComponent } from './components/page-layout/page-layout.component';
import { DashboardLayoutComponent } from './components/dashboard-layout/dashboard-layout.component';
......@@ -132,12 +137,18 @@ import { V2TopbarService } from './layout/v2-topbar/v2-topbar.service';
import { DateDropdownsComponent } from './components/date-dropdowns/date-dropdowns.component';
import { SidebarMarkersService } from './layout/sidebar/markers.service';
import { EmailConfirmationComponent } from './components/email-confirmation/email-confirmation.component';
import { ConfigsService } from './services/configs.service';
import { CookieService } from './services/cookie.service';
import { MetaService } from './services/meta.service';
import { Title, Meta } from '@angular/platform-browser';
import { MediaProxyService } from './services/media-proxy.service';
import { HorizontalFeedService } from './services/horizontal-feed.service';
import { FormInputCheckboxComponent } from './components/forms/checkbox/checkbox.component';
import { AttachmentPasteDirective } from './directives/paste/attachment-paste.directive';
import { V3TopbarComponent } from './layout/v3-topbar/v3-topbar.component';
import { SidebarNavigationService } from './layout/sidebar/navigation.service';
import { UserMenuV3Component } from './layout/v3-topbar/user-menu/user-menu.component';
PlotlyModule.plotlyjs = PlotlyJS;
const routes: Routes = [
{
path: 'email-confirmation',
......@@ -219,6 +230,7 @@ const routes: Routes = [
MaterialBoundSwitchComponent,
IfFeatureDirective,
IfBrowserDirective,
CategoriesSelectorComponent,
CategoriesSelectedComponent,
......@@ -246,6 +258,7 @@ const routes: Routes = [
SwitchComponent,
FeaturedContentComponent,
AttachmentPasteDirective,
PosterDateSelectorComponent,
DraggableListComponent,
ToggleComponent,
......@@ -264,6 +277,7 @@ const routes: Routes = [
ShadowboxSubmitButtonComponent,
EmailConfirmationComponent,
DateDropdownsComponent,
FormInputCheckboxComponent,
],
exports: [
MINDS_PIPES,
......@@ -327,6 +341,7 @@ const routes: Routes = [
MaterialBoundSwitchComponent,
IfFeatureDirective,
IfBrowserDirective,
CategoriesSelectorComponent,
CategoriesSelectedComponent,
......@@ -352,6 +367,7 @@ const routes: Routes = [
SwitchComponent,
NSFWSelectorComponent,
FeaturedContentComponent,
AttachmentPasteDirective,
PosterDateSelectorComponent,
ChannelModeSelectorComponent,
DraggableListComponent,
......@@ -369,16 +385,15 @@ const routes: Routes = [
ShadowboxSubmitButtonComponent,
EmailConfirmationComponent,
DateDropdownsComponent,
FormInputCheckboxComponent,
],
providers: [
SiteService,
SsoService,
AttachmentService,
CookieService,
PagesService,
{
provide: AttachmentService,
useFactory: AttachmentService._,
deps: [Session, Client, Upload, HttpClient],
},
AttachmentService,
{
provide: UpdateMarkersService,
useFactory: (_http, _session, _sockets) => {
......@@ -389,18 +404,10 @@ const routes: Routes = [
{
provide: MindsHttpClient,
useFactory: MindsHttpClient._,
deps: [HttpClient],
},
{
provide: NSFWSelectorCreatorService,
useFactory: _storage => new NSFWSelectorCreatorService(_storage),
deps: [Storage],
},
{
provide: NSFWSelectorConsumerService,
useFactory: _storage => new NSFWSelectorConsumerService(_storage),
deps: [Storage],
deps: [HttpClient, CookieService],
},
NSFWSelectorCreatorService,
NSFWSelectorConsumerService,
{
provide: BoostedContentService,
useFactory: (
......@@ -436,10 +443,39 @@ const routes: Routes = [
useFactory: router => new RouterHistoryService(router),
deps: [Router],
},
{
provide: ConfigsService,
useFactory: client => new ConfigsService(client),
deps: [Client],
},
{
provide: MetaService,
useFactory: (
titleService,
metaService,
siteService,
location,
configsService
) =>
new MetaService(
titleService,
metaService,
siteService,
location,
configsService
),
deps: [Title, Meta, SiteService, Location, ConfigsService],
},
MediaProxyService,
SidebarNavigationService,
{
provide: V2TopbarService,
useFactory: V2TopbarService._,
},
{
provide: SidebarNavigationService,
useFactory: SidebarNavigationService._,
},
{
provide: SidebarMarkersService,
useFactory: SidebarMarkersService._,
......
......@@ -13,7 +13,5 @@ import { Router } from '@angular/router';
`,
})
export class AndroidAppDownloadComponent {
minds = window.Minds;
constructor(private router: Router) {}
}
......@@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, OnInit } from '@angular/core';
import { Storage } from '../../../services/storage';
import { Client } from '../../../services/api';
import { CookieService } from '../../../common/services/cookie.service';
@Component({
selector: 'm-announcement',
......@@ -21,21 +22,21 @@ import { Client } from '../../../services/api';
`,
})
export class AnnouncementComponent implements OnInit {
minds: Minds = window.Minds;
hidden: boolean = false;
@Input() id: string = 'default';
@Input() canClose: boolean = true;
@Input() remember: boolean = true;
constructor(private storage: Storage) {}
constructor(private cookieService: CookieService) {}
ngOnInit() {
if (this.storage.get('hide-announcement:' + this.id)) this.hidden = true;
if (this.cookieService.get('hide-announcement:' + this.id) === '1')
this.hidden = true;
}
close() {
if (this.remember) {
this.storage.set('hide-announcement:' + this.id, true);
this.cookieService.put('hide-announcement:' + this.id, '1');
}
this.hidden = true;
......
......@@ -2,9 +2,7 @@
<a href="javascript:;" (click)="selectChoice.next(choice)">
<img
class="m-postAutocompleteItemRenderer__avatar mdl-shadow--2dp"
[src]="
minds.cdn_url + 'icon/' + choice.guid + '/medium/' + choice.icontime
"
[src]="cdnUrl + 'icon/' + choice.guid + '/medium/' + choice.icontime"
/>
{{ choice.username }}
</a>
......