refactor with bootstrap 4
This commit is contained in:
parent
b486aff415
commit
14d0253ccb
5
.babelrc
5
.babelrc
|
@ -1,3 +1,6 @@
|
||||||
{
|
{
|
||||||
"presets": ["es2015"]
|
"presets": [
|
||||||
|
"es2015",
|
||||||
|
"stage-0"
|
||||||
|
]
|
||||||
}
|
}
|
25
.eslintrc
Normal file
25
.eslintrc
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true,
|
||||||
|
"modules": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"rules": {
|
||||||
|
// "quotes": [2, "single"],
|
||||||
|
"jsx-quotes": 2,
|
||||||
|
"strict": [
|
||||||
|
2,
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"react/jsx-uses-react": 2,
|
||||||
|
"react/jsx-uses-vars": 2,
|
||||||
|
"react/react-in-jsx-scope": 2
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
]
|
||||||
|
}
|
52
.jshintrc
52
.jshintrc
|
@ -1,52 +0,0 @@
|
||||||
{
|
|
||||||
"esnext": true,
|
|
||||||
"camelcase": true,
|
|
||||||
"devel": false,
|
|
||||||
"asi": false,
|
|
||||||
"boss": false,
|
|
||||||
"eqnull": false,
|
|
||||||
"es5": true,
|
|
||||||
"moz": false,
|
|
||||||
"evil": false,
|
|
||||||
"expr": true,
|
|
||||||
"funcscope": true,
|
|
||||||
"iterator": false,
|
|
||||||
"lastsemic": false,
|
|
||||||
"laxbreak": false,
|
|
||||||
"laxcomma": false,
|
|
||||||
"loopfunc": false,
|
|
||||||
"multistr": true,
|
|
||||||
"noyield": false,
|
|
||||||
"notypeof": false,
|
|
||||||
"proto": false,
|
|
||||||
"scripturl": false,
|
|
||||||
"shadow": false,
|
|
||||||
"sub": false,
|
|
||||||
"supernew": false,
|
|
||||||
"validthis": false,
|
|
||||||
"plusplus": false,
|
|
||||||
"jquery": true,
|
|
||||||
"mocha": false,
|
|
||||||
"node": true,
|
|
||||||
"latedef": true,
|
|
||||||
"quotmark": "single",
|
|
||||||
"maxparams": 6,
|
|
||||||
"maxdepth": 5,
|
|
||||||
"maxerr": 50,
|
|
||||||
"globalstrict": false,
|
|
||||||
"globals": {
|
|
||||||
"inject": true,
|
|
||||||
"angular": true,
|
|
||||||
"require": true,
|
|
||||||
"process": true,
|
|
||||||
"module": true,
|
|
||||||
"describe": true,
|
|
||||||
"beforeEach": true,
|
|
||||||
"afterEach": true,
|
|
||||||
"it": true,
|
|
||||||
"expect": true,
|
|
||||||
"__dirname": true,
|
|
||||||
"exports": true,
|
|
||||||
"spyOn": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "node"
|
|
||||||
before_script:
|
|
||||||
- npm install -g gulp-cli
|
|
||||||
script: gulp build
|
|
3
build.sh
3
build.sh
|
@ -7,7 +7,6 @@ composer install
|
||||||
cp -rv src/{backend.php,config.sample.php} dist/
|
cp -rv src/{backend.php,config.sample.php} dist/
|
||||||
|
|
||||||
# build Javascript frontend
|
# build Javascript frontend
|
||||||
npm install
|
npm run build
|
||||||
gulp build
|
|
||||||
|
|
||||||
echo "done"
|
echo "done"
|
57
dist/bundle_65d8f46de4091cada75c.js
vendored
Normal file
57
dist/bundle_65d8f46de4091cada75c.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/index.html
vendored
2
dist/index.html
vendored
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html> <html lang=de> <head> <meta charset=utf-8> <title>Mailbox</title> <meta name=viewport content="width=device-width,initial-scale=1"> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=black> <meta name=description content=Mailbox> <link rel=icon href="data:;base64,iVBORw0KGgo="> </head> <body ng-app=app ng-cloak> <div ui-view></div> <footer> <p>Powered by <a href=https://github.com/synox/disposable-mailbox><strong>synox/disposable-mailbox</strong></a> | <a href=https://github.com/synox/disposable-mailbox><span class="octicon octicon-mark-github"></span> Fork me on github</a></p> </footer> <script type="text/javascript" src="mailbox_1ad13ef2ffe2df84a4e6.js"></script></body> </html>
|
<!DOCTYPE html> <html lang=de> <head> <meta charset=utf-8> <title>Mailbox</title> <meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=black> <meta name=description content=Mailbox> <link rel=icon href="data:;base64,iVBORw0KGgo="> </head> <body ng-app=app ng-cloak> <app></app> <footer> <p>Powered by <a href=https://github.com/synox/disposable-mailbox><strong>synox/disposable-mailbox</strong></a> | <a href=https://github.com/synox/disposable-mailbox><span class="octicon octicon-mark-github"></span> Fork me on github</a></p> </footer> <script type="text/javascript" src="bundle_65d8f46de4091cada75c.js"></script></body> </html>
|
53
dist/mailbox_1ad13ef2ffe2df84a4e6.js
vendored
53
dist/mailbox_1ad13ef2ffe2df84a4e6.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,73 +0,0 @@
|
||||||
import gulp from 'gulp';
|
|
||||||
import path from 'path';
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import WebpackDevServer from 'webpack-dev-server';
|
|
||||||
import karma from 'karma';
|
|
||||||
import ip from 'ip';
|
|
||||||
import webpackConfig from './webpack.config';
|
|
||||||
import del from 'del';
|
|
||||||
|
|
||||||
|
|
||||||
gulp.task('clean', function () {
|
|
||||||
return del([
|
|
||||||
'dist/*.js',
|
|
||||||
'target/build']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gulp-Task: Fuehrt webpack aus und startet den Development-Server
|
|
||||||
*/
|
|
||||||
gulp.task('dev-server', () => {
|
|
||||||
return new WebpackDevServer(webpack(webpackConfig.development), {
|
|
||||||
hot: true,
|
|
||||||
contentBase: './dist/',
|
|
||||||
watchOptions: {
|
|
||||||
aggregateTimeout: 100, poll: 300
|
|
||||||
}, stats: {
|
|
||||||
colors: true
|
|
||||||
}
|
|
||||||
}).listen(3000, 'localhost', function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gulp-Task: Fuehrt die Karma-Tests auf dem PhantomJS Browser aus
|
|
||||||
*/
|
|
||||||
gulp.task('test-phantomjs', (done) => {
|
|
||||||
|
|
||||||
let hostname = process.env.host || ip.address();
|
|
||||||
let externalport = process.env.externalport || 7777;
|
|
||||||
|
|
||||||
return new karma.Server({
|
|
||||||
configFile: __dirname + '/karma.conf.js',
|
|
||||||
hostname: hostname,
|
|
||||||
port: externalport,
|
|
||||||
browsers: ['PhantomJS']
|
|
||||||
}, done).start();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
gulp.task('webpack-prod', [], (done) => {
|
|
||||||
return webpack(webpackConfig.production, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
gulp.task('build', ['test-phantomjs', 'clean', 'webpack-prod'], (done) => {
|
|
||||||
return gulp
|
|
||||||
.src(path.join('target', 'build', '*'))
|
|
||||||
.pipe(gulp.dest('dist'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('build-skipTests', ['clean', 'webpack-prod'], (done) => {
|
|
||||||
return gulp
|
|
||||||
.src(path.join('target', 'build', '*'))
|
|
||||||
.pipe(gulp.dest('dist'));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
gulp.task('default', ['dev-server']);
|
|
112
karma.conf.js
112
karma.conf.js
|
@ -1,112 +0,0 @@
|
||||||
var ip = require('ip');
|
|
||||||
var webjsConfig = require('./shared.build.config');
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
|
|
||||||
var seleniumWebgrid = {
|
|
||||||
hostname: 'webtestgrid.myhost.com',
|
|
||||||
port: 4444
|
|
||||||
};
|
|
||||||
|
|
||||||
config.set({
|
|
||||||
|
|
||||||
hostname: ip.address(),
|
|
||||||
|
|
||||||
basePath: __dirname,
|
|
||||||
|
|
||||||
// frameworks to use
|
|
||||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
|
||||||
frameworks: ['jasmine'],
|
|
||||||
|
|
||||||
/* list of files/patterns to load in the browser
|
|
||||||
Fuer den Phantomjs wird fuer die Methode 'bind' ein Polyfill geladen
|
|
||||||
da der Browser die Methode nicht kennt und diese von den Frameworks verwendet wird */
|
|
||||||
files: [
|
|
||||||
'./node_modules/phantomjs-polyfill/bind-polyfill.js', {
|
|
||||||
pattern: 'spec.bundle.js', watched: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// files to exclude
|
|
||||||
exclude: [],
|
|
||||||
|
|
||||||
// preprocess matching files before serving them to the browser
|
|
||||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
|
||||||
preprocessors: {
|
|
||||||
'spec.bundle.js': ['webpack', 'sourcemap', 'coverage']
|
|
||||||
},
|
|
||||||
|
|
||||||
webpack: {
|
|
||||||
debug: true, devtool: 'inline-source-map', module: {
|
|
||||||
loaders: webjsConfig.webpackLoaders
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
webpackServer: {
|
|
||||||
// prevent console spamming when running in Karma!
|
|
||||||
noInfo: true
|
|
||||||
},
|
|
||||||
|
|
||||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
|
||||||
reporters: ['progress', 'junit', 'coverage'],
|
|
||||||
|
|
||||||
// enable colors in the output
|
|
||||||
colors: true,
|
|
||||||
|
|
||||||
// level of logging
|
|
||||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
|
|
||||||
// toggle whether to watch files and rerun tests upon incurring changes
|
|
||||||
autoWatch: false,
|
|
||||||
|
|
||||||
// Browser-Konfiguration auf dem Selenium Grid
|
|
||||||
customLaunchers: {
|
|
||||||
'SeleniumCH': {
|
|
||||||
base: 'WebDriver',
|
|
||||||
config: seleniumWebgrid,
|
|
||||||
browserName: 'chrome'
|
|
||||||
},
|
|
||||||
'SeleniumFF': {
|
|
||||||
base: 'WebDriver',
|
|
||||||
config: seleniumWebgrid,
|
|
||||||
browserName: 'firefox'
|
|
||||||
},
|
|
||||||
'SeleniumIE': {
|
|
||||||
base: 'WebDriver',
|
|
||||||
config: seleniumWebgrid,
|
|
||||||
browserName: 'internet explorer',
|
|
||||||
'x-ua-compatible': 'IE=edge'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// start these browsers
|
|
||||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
|
||||||
browsers: ['PhantomJS'], // Test auf dem PhantomJS
|
|
||||||
// browsers: ['SeleniumFF', 'SeleniumCH', 'SeleniumIE'], // Test auf dem Selenium-Webgrid
|
|
||||||
|
|
||||||
// if true, Karma runs tests once and exits
|
|
||||||
singleRun: true,
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
'karma-junit-reporter',
|
|
||||||
'karma-jasmine',
|
|
||||||
'karma-coverage',
|
|
||||||
'karma-phantomjs-launcher',
|
|
||||||
'karma-webpack',
|
|
||||||
'karma-sourcemap-loader',
|
|
||||||
'karma-webdriver-launcher'
|
|
||||||
],
|
|
||||||
|
|
||||||
// Coverage & JUnit Report fuer SonarQube
|
|
||||||
junitReporter: {
|
|
||||||
outputDir: 'target/surefire', suite: 'unit'
|
|
||||||
}, coverageReporter: {
|
|
||||||
reporters: [
|
|
||||||
{
|
|
||||||
type: 'lcov', dir: 'target/surefire', subdir: '.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
99
package.json
99
package.json
|
@ -2,65 +2,52 @@
|
||||||
"name": "disposable-mailbox",
|
"name": "disposable-mailbox",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "disposable-mailbox",
|
"description": "disposable-mailbox",
|
||||||
"homepage": "https://github.com/synox/disposable-mailbox",
|
"scripts": {
|
||||||
"dependencies": {
|
"start": "node server.js",
|
||||||
"angular": "^1.5.2",
|
"build": "NODE_ENV=production webpack -p",
|
||||||
"angular-sanitize": "^1.5.6",
|
"size": "NODE_ENV=production webpack --json | webpack-bundle-size-analyzer ",
|
||||||
"angular-stickyfill": "^0.1.0",
|
"lint": "./node_modules/eslint/bin/eslint.js src"
|
||||||
"angular-ui-bootstrap": "^1.3.3",
|
|
||||||
"angular-ui-router": "^1.0.0-beta.1",
|
|
||||||
"autolinker": "^0.27.0",
|
|
||||||
"babel-polyfill": "^6.9.1",
|
|
||||||
"bootstrap": "^3.3.6",
|
|
||||||
"phonetic": "^0.1.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"angular-mocks": "^1.5.2",
|
|
||||||
"babel-core": "^6.10.4",
|
|
||||||
"babel-loader": "^6.2.4",
|
|
||||||
"babel-preset-es2015": "^6.9.0",
|
|
||||||
"browser-sync": "^2.10.0",
|
|
||||||
"browser-sync-webpack-plugin": "^1.0.0",
|
|
||||||
"css-loader": "^0.23.1",
|
|
||||||
"del": "^2.2.1",
|
|
||||||
"file-loader": "^0.9.0",
|
|
||||||
"gulp": "^3.9.0",
|
|
||||||
"gulp-esdoc": "^0.2.0",
|
|
||||||
"gulp-file-inline": "^1.3.6",
|
|
||||||
"html-loader": "^0.4.3",
|
|
||||||
"html-webpack-plugin": "^2.15.0",
|
|
||||||
"ip": "^1.0.2",
|
|
||||||
"isparta-loader": "^2.0.0",
|
|
||||||
"jasmine-core": "^2.3.4",
|
|
||||||
"json-loader": "^0.5.3",
|
|
||||||
"karma": "^1.1.0",
|
|
||||||
"karma-coverage": "^1.0.0",
|
|
||||||
"karma-jasmine": "~1.0.2",
|
|
||||||
"karma-junit-reporter": "^1.1.0",
|
|
||||||
"karma-phantomjs-launcher": "^1.0.1",
|
|
||||||
"karma-sourcemap-loader": "^0.3.4",
|
|
||||||
"karma-webdriver-launcher": "^1.0.4",
|
|
||||||
"karma-webpack": "^1.5.1",
|
|
||||||
"ng-annotate-webpack-plugin": "^0.1.2",
|
|
||||||
"node-libs-browser": "^1.0.0",
|
|
||||||
"node-sass": "^3.8.0",
|
|
||||||
"node.extend": "^1.1.5",
|
|
||||||
"phantomjs": "^2.1.7",
|
|
||||||
"phantomjs-polyfill": "0.0.2",
|
|
||||||
"proxy-middleware": "^0.15.0",
|
|
||||||
"raw-loader": "^0.5.1",
|
|
||||||
"sass-loader": "^4.0.0",
|
|
||||||
"style-loader": "^0.13.0",
|
|
||||||
"url-loader": "^0.5.6",
|
|
||||||
"webpack": "^1.13.1",
|
|
||||||
"webpack-dev-server": "^1.12.1"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/synox/disposable-mailbox.git"
|
"url": "https://github.com/synox/disposable-mailbox.git"
|
||||||
},
|
},
|
||||||
"license": "CC-BY-NC-SA-4.0",
|
"homepage": "https://github.com/synox/disposable-mailbox",
|
||||||
"scripts": {
|
"dependencies": {
|
||||||
"test": "gulp test-phantomjs"
|
"angular": "^1.5.2",
|
||||||
}
|
"angular-sanitize": "^1.5.6",
|
||||||
|
"angular-stickyfill": "^0.1.0",
|
||||||
|
"hasher": "^1.2.0",
|
||||||
|
"autolinker": "^0.27.0",
|
||||||
|
"babel-polyfill": "^6.9.1",
|
||||||
|
"bootstrap": "^4.0.0-alpha.3",
|
||||||
|
"phonetic": "^0.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-core": "^6.0.20",
|
||||||
|
"babel-eslint": "^4.1.3",
|
||||||
|
"babel-loader": "^6.0.1",
|
||||||
|
"babel-preset-es2015": "^6.0.15",
|
||||||
|
"babel-preset-react": "^6.0.15",
|
||||||
|
"babel-preset-stage-0": "^6.0.15",
|
||||||
|
"css-loader": "^0.23.1",
|
||||||
|
"eslint": "^1.10.3",
|
||||||
|
"eslint-plugin-react": "^3.6.2",
|
||||||
|
"file-loader": "^0.9.0",
|
||||||
|
"html-loader": "^0.4.3",
|
||||||
|
"html-webpack-plugin": "^2.15.0",
|
||||||
|
"json-loader": "^0.5.4",
|
||||||
|
"node-sass": "^3.8.0",
|
||||||
|
"react-hot-loader": "^1.3.0",
|
||||||
|
"sass-loader": "^4.0.0",
|
||||||
|
"style-loader": "^0.13.0",
|
||||||
|
"uglify-loader": "^1.3.0",
|
||||||
|
"url-loader": "^0.5.6",
|
||||||
|
"webpack": "^1.12.2",
|
||||||
|
"webpack-bundle-size-analyzer": "^2.0.2",
|
||||||
|
"webpack-dev-server": "^1.12.1",
|
||||||
|
"webpack-merge": "^0.14.1",
|
||||||
|
"webpack-validator": "^2.2.3"
|
||||||
|
},
|
||||||
|
"license": "CC-BY-NC-SA-4.0"
|
||||||
}
|
}
|
||||||
|
|
14
server.js
Normal file
14
server.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var WebpackDevServer = require('webpack-dev-server');
|
||||||
|
var config = require('./webpack.config');
|
||||||
|
|
||||||
|
new WebpackDevServer(webpack(config), {
|
||||||
|
hot: false,
|
||||||
|
historyApiFallback: true
|
||||||
|
}).listen(3000, 'localhost', function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
return console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Listening at http://localhost:3000/');
|
||||||
|
});
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* @author u215942 (Stefan Zeller)
|
|
||||||
* @version: 1.0.1
|
|
||||||
* @since 04.04.2016
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.webpackLoaders = [
|
|
||||||
{
|
|
||||||
test: /\.js$/, exclude: [/node_modules/],
|
|
||||||
loader: 'babel',
|
|
||||||
query: {
|
|
||||||
// https://github.com/babel/babel-loader#options
|
|
||||||
cacheDirectory: true,
|
|
||||||
presets: ['es2015']
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
test: /\.json$/, loader: 'json'
|
|
||||||
}, {
|
|
||||||
test: /\.html$/, loader: 'html'
|
|
||||||
}, {
|
|
||||||
test: /\.css$/, loader: 'style!css'
|
|
||||||
}, {
|
|
||||||
test: /\.scss$/, loader: 'style!css!sass'
|
|
||||||
}, {
|
|
||||||
test: /\.(jpe?g|png|gif|svg)$/i, loader: 'url'
|
|
||||||
}, {
|
|
||||||
test: /\.(woff|woff2)$/, loader: 'url?mimetype=application/font-woff'
|
|
||||||
}, {
|
|
||||||
test: /\.ttf$/, loader: 'url'
|
|
||||||
}, {
|
|
||||||
test: /\.eot$/, loader: 'url'
|
|
||||||
}
|
|
||||||
];
|
|
|
@ -1,5 +0,0 @@
|
||||||
import 'angular';
|
|
||||||
import 'angular-mocks';
|
|
||||||
|
|
||||||
let context = require.context('./src/mailbox', true, /\.spec\.js/);
|
|
||||||
context.keys().forEach(context);
|
|
117
src/app.js
117
src/app.js
|
@ -1,20 +1,123 @@
|
||||||
// Vendor-Imports
|
// Vendor-Imports
|
||||||
import angular from "angular";
|
import angular from "angular";
|
||||||
import uiRouter from "angular-ui-router";
|
import "bootstrap/scss/bootstrap.scss";
|
||||||
import uiBootstrap from "angular-ui-bootstrap";
|
|
||||||
import "bootstrap/dist/css/bootstrap.css";
|
|
||||||
import "babel-polyfill";
|
import "babel-polyfill";
|
||||||
import angularStickyfill from "angular-stickyfill";
|
import angularStickyfill from "angular-stickyfill";
|
||||||
import "angular-stickyfill/dist/angular-stickyfill.css";
|
import "angular-stickyfill/dist/angular-stickyfill.css";
|
||||||
import Mailbox from "./mailbox/mailbox";
|
import {cleanUsername, generateRandomUsername} from "./util";
|
||||||
|
import hasher from "hasher";
|
||||||
|
import Header from "./components/header/header";
|
||||||
|
import List from "./components/list/list";
|
||||||
|
import Mail from "./components/mail/mail";
|
||||||
|
|
||||||
|
|
||||||
|
class AppController {
|
||||||
|
/*@ngInject*/
|
||||||
|
constructor($http, $log, config, $interval) {
|
||||||
|
this.$interval = $interval;
|
||||||
|
this.$http = $http;
|
||||||
|
this.config = config;
|
||||||
|
this.$log = $log;
|
||||||
|
this.$log.log('start controller');
|
||||||
|
this.address = null;
|
||||||
|
this.username = null;
|
||||||
|
this.mails = [];
|
||||||
|
this.state = {isUpdating: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.$log.debug("init");
|
||||||
|
|
||||||
|
hasher.changed.add(this.onHashChange.bind(this));
|
||||||
|
hasher.initialized.add(this.onHashChange.bind(this)); //add initialized listener (to grab initial value in case it is already set)
|
||||||
|
hasher.init(); //initialize hasher (start listening for history changes)
|
||||||
|
|
||||||
|
this.intervalPromise = this.$interval(() => this.updateMails(), this.config.reload_interval_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
this.$interval.cancel(this.intervalPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
onHashChange(hash) {
|
||||||
|
this.updateUsername(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeUsername({username}) {
|
||||||
|
this.updateUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGotoRandom() {
|
||||||
|
let username = generateRandomUsername();
|
||||||
|
this.updateUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEmails(username) {
|
||||||
|
return this.$http.get(this.config.backend_url, {params: {username: username, action: "get"}})
|
||||||
|
.then(response=> {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEmailsAsync(username) {
|
||||||
|
this.$log.debug("updating mails for ", username);
|
||||||
|
this.state.isUpdating = true;
|
||||||
|
this.loadEmails(this.username).then(data=> {
|
||||||
|
this.mails = data.mails;
|
||||||
|
this.address = data.address;
|
||||||
|
this.username = data.username;
|
||||||
|
this.state.isUpdating = false;
|
||||||
|
this.$log.debug("received mails for ", username);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMails() {
|
||||||
|
if (this.username) {
|
||||||
|
this.loadEmailsAsync(this.username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUsername(username) {
|
||||||
|
this.username = cleanUsername(username);
|
||||||
|
if (this.username.length > 0) {
|
||||||
|
hasher.setHash(this.username);
|
||||||
|
this.address = this.username; // use username until real address has been loaded
|
||||||
|
this.updateMails();
|
||||||
|
} else {
|
||||||
|
this.address = null;
|
||||||
|
this.mails = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Interne Modul-Imports
|
|
||||||
|
|
||||||
angular.module('app', [
|
angular.module('app', [
|
||||||
uiRouter, uiBootstrap, Mailbox.name, angularStickyfill
|
List, Mail, angularStickyfill, Header
|
||||||
])
|
])
|
||||||
|
|
||||||
|
.component('app', {
|
||||||
|
template: `
|
||||||
|
<header
|
||||||
|
username="$ctrl.username"
|
||||||
|
address="$ctrl.address"
|
||||||
|
mailcount="$ctrl.mails.length"
|
||||||
|
on-change-username="$ctrl.onChangeUsername($event)"
|
||||||
|
on-goto-random="$ctrl.onGotoRandom($event)">
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<inbox
|
||||||
|
mails="$ctrl.mails"
|
||||||
|
username="$ctrl.username"
|
||||||
|
address="$ctrl.address"
|
||||||
|
state="$ctrl.state">
|
||||||
|
</inbox>
|
||||||
|
`,
|
||||||
|
controller: AppController
|
||||||
|
})
|
||||||
|
|
||||||
.constant('config', {
|
.constant('config', {
|
||||||
'backend_url': 'http://dubgo.com/m2/backend.php',
|
'backend_url': './backend.php',
|
||||||
'reload_interval_ms': 10000
|
'reload_interval_ms': 10000
|
||||||
})
|
})
|
||||||
|
|
82
src/components/header/header.js
Normal file
82
src/components/header/header.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import angular from "angular";
|
||||||
|
import "./header.scss";
|
||||||
|
|
||||||
|
class HeaderController {
|
||||||
|
/*@ngInject*/
|
||||||
|
constructor($log) {
|
||||||
|
this.$log = $log;
|
||||||
|
this.inputFieldUsername = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.inputFieldUsername = this.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
$onChanges(changes) {
|
||||||
|
if (changes.address) {
|
||||||
|
this.inputFieldUsername = this.username;
|
||||||
|
this.address = this.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoMailbox(username) {
|
||||||
|
this.onChangeUsername({
|
||||||
|
$event: {
|
||||||
|
username: username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
randomize() {
|
||||||
|
this.onGotoRandom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderComponent = {
|
||||||
|
bindings: {
|
||||||
|
address: '<',
|
||||||
|
username: '<',
|
||||||
|
mailcount: '<',
|
||||||
|
onChangeUsername: '&',
|
||||||
|
onGotoRandom: '&'
|
||||||
|
},
|
||||||
|
controller: HeaderController,
|
||||||
|
template: `
|
||||||
|
<div class="nav-container">
|
||||||
|
<div class="container">
|
||||||
|
<nav class="navbar navbar-light">
|
||||||
|
<a class="navbar-brand"><span class="octicon-inbox"></span>
|
||||||
|
|
||||||
|
{{$ctrl.address}}
|
||||||
|
|
||||||
|
<span ng-if="$ctrl.mailcount" class="tag tag-pill tag-default">{{$ctrl.mailcount}}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
|
||||||
|
<button type="button nav-link" class="btn btn-outline-primary pull-xs-right"
|
||||||
|
ng-click="$ctrl.randomize()">
|
||||||
|
<span class="glyphicon glyphicon-random"></span>
|
||||||
|
randomize
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<form class="form-inline pull-xs-right" ng-submit="$ctrl.gotoMailbox($ctrl.inputFieldUsername)">
|
||||||
|
<input ng-model="$ctrl.inputFieldUsername"
|
||||||
|
placeholder="username"
|
||||||
|
type="text" class="form-control"/>
|
||||||
|
<button type="submit" class="btn btn-outline-success">login</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default angular
|
||||||
|
.module('header', [])
|
||||||
|
.component('header', HeaderComponent)
|
||||||
|
.name;
|
11
src/components/header/header.scss
Normal file
11
src/components/header/header.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.nav-container {
|
||||||
|
background-color: #D9E2E9;
|
||||||
|
//heigh: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.octicon-inbox {
|
||||||
|
display: inline-block;
|
||||||
|
width: 26px;
|
||||||
|
height: 23px;
|
||||||
|
background: url('octicon-inbox.gif') no-repeat;
|
||||||
|
}
|
BIN
src/components/header/octicon-inbox.gif
Normal file
BIN
src/components/header/octicon-inbox.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 262 B |
25
src/components/list/list.html
Normal file
25
src/components/list/list.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<main>
|
||||||
|
<div class="container min-height">
|
||||||
|
<p ng-if="!$ctrl.username">
|
||||||
|
Use the buttons above to create a new inbox, or open a specific mailbox.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div ng-if="$ctrl.username && $ctrl.mails.length == 0">
|
||||||
|
<div class="waiting-screen">
|
||||||
|
<h1>{{$ctrl.address}}</h1>
|
||||||
|
<p class="lead">Inbox is empty.</p>
|
||||||
|
<p><br/>
|
||||||
|
<img src="spinner.gif">
|
||||||
|
<br/></p>
|
||||||
|
<p class="lead">Emails to {{address}} will be automatically displayed on this page. </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div ng-if="$ctrl.username" ng-repeat="mail in $ctrl.mails | orderBy:'-date' track by $index"
|
||||||
|
class="email-table">
|
||||||
|
<mail mail="mail"></mail>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
29
src/components/list/list.js
Normal file
29
src/components/list/list.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import angular from "angular";
|
||||||
|
import template from "./list.html";
|
||||||
|
import "./list.scss";
|
||||||
|
|
||||||
|
|
||||||
|
class ListController {
|
||||||
|
/*@ngInject*/
|
||||||
|
constructor($log) {
|
||||||
|
this.$log = $log;
|
||||||
|
|
||||||
|
// @Input:
|
||||||
|
// this.mails = [];
|
||||||
|
// this.username = null;
|
||||||
|
// this.address = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default angular.module('mailbox.inbox', [])
|
||||||
|
.component('inbox', {
|
||||||
|
template, controller: ListController,
|
||||||
|
bindings: {
|
||||||
|
address: '<',
|
||||||
|
username: '<',
|
||||||
|
mails: '<',
|
||||||
|
state: '<'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.name;
|
18
src/components/list/list.scss
Normal file
18
src/components/list/list.scss
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer p {
|
||||||
|
margin-top: 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-table {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.min-height {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
38
src/components/mail/mail.html
Normal file
38
src/components/mail/mail.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<section class="email">
|
||||||
|
<div class="row sticky-header" ec-stickyfill>
|
||||||
|
<div class="col-sm-12 email-summary">{{$ctrl.mail.subject}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 email-headers">
|
||||||
|
<dl>
|
||||||
|
<dt>To:</dt>
|
||||||
|
<dd>{{$ctrl.mail.toString}}</dd>
|
||||||
|
<div ng-if="$ctrl.mail.cc" ng-repeat="(address,name) in $ctrl.mail.cc">
|
||||||
|
<dt>CC:</dt>
|
||||||
|
<dd>{{address}}</dd>
|
||||||
|
</div>
|
||||||
|
<dt>From:</dt>
|
||||||
|
<dd>{{$ctrl.mail.fromName}} <{{$ctrl.mail.fromAddress}}></dd>
|
||||||
|
<dt>Date:</dt>
|
||||||
|
<dd>{{$ctrl.mail.date}}</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mail-content" ng-init="htmlTabActive=false">
|
||||||
|
|
||||||
|
|
||||||
|
<button ng-show="htmlTabActive" class="btn btn-outline-info btn-sm" ng-click="htmlTabActive=false">show text
|
||||||
|
</button>
|
||||||
|
<button ng-show="$ctrl.mail.textHtml && !htmlTabActive" class="btn btn-outline-info btn-sm"
|
||||||
|
ng-click="htmlTabActive=true">show html
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div ng-if="!htmlTabActive" ng-bind-html="$ctrl.mail.textPlain | nl2br | autolink "></div>
|
||||||
|
<div ng-if="htmlTabActive" class="mail-conent" ng-bind-html="$ctrl.mail.textHtml"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
import angular from 'angular';
|
import angular from "angular";
|
||||||
import uiRouter from 'angular-ui-router';
|
import ngSanitize from "angular-sanitize";
|
||||||
import ngSanitize from 'angular-sanitize';
|
import Autolinker from "autolinker";
|
||||||
import Autolinker from 'autolinker';
|
import template from "./mail.html";
|
||||||
|
import "./mail.scss";
|
||||||
import template from './mail.html';
|
|
||||||
import controller from './mail.controller';
|
|
||||||
import './mail.scss';
|
|
||||||
|
|
||||||
|
|
||||||
export default angular.module('mailbox.inbox.mail', [uiRouter, ngSanitize])
|
class MailController {
|
||||||
|
/*@ngInject*/
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default angular.module('mailbox.inbox.mail', [ngSanitize])
|
||||||
.component('mail', {
|
.component('mail', {
|
||||||
template, controller,
|
template,
|
||||||
|
controller: MailController,
|
||||||
bindings: {
|
bindings: {
|
||||||
mail: '='
|
mail: '<'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// http://stackoverflow.com/a/20033625/79461
|
// http://stackoverflow.com/a/20033625/79461
|
||||||
|
@ -29,4 +34,4 @@ export default angular.module('mailbox.inbox.mail', [uiRouter, ngSanitize])
|
||||||
return Autolinker.link(data, {truncate: {length: 50, location: 'middle', newWindow: true}});
|
return Autolinker.link(data, {truncate: {length: 50, location: 'middle', newWindow: true}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
).name;
|
55
src/components/mail/mail.scss
Normal file
55
src/components/mail/mail.scss
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
$email-border-color: #7C96AB;
|
||||||
|
$email-summary-background: white;
|
||||||
|
$email-header-background: white;
|
||||||
|
|
||||||
|
.sticky-header {
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email {
|
||||||
|
.email-summary {
|
||||||
|
font-weight: bold;
|
||||||
|
border-top: 5px solid $email-border-color;
|
||||||
|
border-bottom: 1px solid $email-border-color;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: $email-summary-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-headers {
|
||||||
|
background-color: $email-header-background;
|
||||||
|
|
||||||
|
dl {
|
||||||
|
padding: 0 0 4px;
|
||||||
|
color: black;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
dt {
|
||||||
|
float: left;
|
||||||
|
color: black;
|
||||||
|
margin: 0 3px 0 0;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: .3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-content {
|
||||||
|
background-color: white;
|
||||||
|
padding-left: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-screen {
|
||||||
|
padding: 40px 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.min-height {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Mailbox</title>
|
<title>Mailbox</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
@ -13,12 +13,14 @@
|
||||||
</head>
|
</head>
|
||||||
<body ng-app="app" ng-cloak>
|
<body ng-app="app" ng-cloak>
|
||||||
|
|
||||||
<div ui-view></div>
|
<app></app>
|
||||||
|
|
||||||
|
<!--<div ui-view></div>-->
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Powered by <a href="https://github.com/synox/disposable-mailbox"><strong>synox/disposable-mailbox</strong></a>
|
<p>Powered by <a href="https://github.com/synox/disposable-mailbox"><strong>synox/disposable-mailbox</strong></a>
|
||||||
| <a href="https://github.com/synox/disposable-mailbox"><span class="octicon octicon-mark-github"></span> Fork me on github</a></p>
|
| <a href="https://github.com/synox/disposable-mailbox"><span class="octicon octicon-mark-github"></span> Fork
|
||||||
|
me on github</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
import phonetic from 'phonetic';
|
|
||||||
|
|
||||||
class MailboxController {
|
|
||||||
/*@ngInject*/
|
|
||||||
constructor($log, $interval, config, mailboxService, $rootScope, $stateParams, $state) {
|
|
||||||
this.$rootScope = $rootScope;
|
|
||||||
this.$log = $log;
|
|
||||||
this.$interval = $interval;
|
|
||||||
this.config = config;
|
|
||||||
this.mailboxService = mailboxService;
|
|
||||||
this.mails = [];
|
|
||||||
this.$stateParams = $stateParams;
|
|
||||||
this.$state = $state;
|
|
||||||
this.address = null; // is set when mails are loaded
|
|
||||||
this.state = 'home';
|
|
||||||
}
|
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
if (this.getCurrentUsername()) {
|
|
||||||
this.state = 'loading';
|
|
||||||
this.address = this.getCurrentUsername(); // use username until real address has been loaded
|
|
||||||
this.intervalPromise = this.$interval(() => this.loadMails(), this.config.reload_interval_ms);
|
|
||||||
this.loadMails();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static cleanUsername(username) {
|
|
||||||
return username.replace(/[@].*$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
gotoMailbox(username) {
|
|
||||||
username = MailboxController.cleanUsername(username);
|
|
||||||
this.address = username; // use username until real address has been loaded
|
|
||||||
this.$state.go('inbox', {username: username});
|
|
||||||
}
|
|
||||||
|
|
||||||
gotoRandomAddress() {
|
|
||||||
let username = this.generateRandomUsername();
|
|
||||||
this.gotoMailbox(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateRandomUsername() {
|
|
||||||
let username = phonetic.generate({syllables: 3, phoneticSimplicity: 1});
|
|
||||||
if (Math.random() >= 0.5) {
|
|
||||||
username += this.getRandomInt(30, 99);
|
|
||||||
}
|
|
||||||
return username.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
getRandomInt(min, max) {
|
|
||||||
return Math.floor(Math.random() * (max - min)) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
$onDestroy() {
|
|
||||||
this.$log.debug("destroying controller");
|
|
||||||
this.$interval.cancel(this.intervalPromise);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
loadMails() {
|
|
||||||
this.mailboxService.loadEmails(this.getCurrentUsername())
|
|
||||||
.then(data => {
|
|
||||||
|
|
||||||
this.mails = data.mails;
|
|
||||||
if (this.mails.length !== 0) {
|
|
||||||
this.state = 'list';
|
|
||||||
} else {
|
|
||||||
this.state = 'empty';
|
|
||||||
}
|
|
||||||
this.address = data.address;
|
|
||||||
this.loadingData = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getCurrentUsername() {
|
|
||||||
return this.$stateParams.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MailboxController;
|
|
|
@ -1,77 +0,0 @@
|
||||||
<div class="navbar" role="navigation">
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<div class="hidden-xs">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<a class="navbar-brand"><span class="octicon-inbox"></span> Mailbox</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="navbar-form navbar-left">
|
|
||||||
<a class="btn btn-default" ng-click="$ctrl.gotoRandomAddress()" role="button">
|
|
||||||
<span class="glyphicon glyphicon-random" aria-hidden="true"></span>
|
|
||||||
create random
|
|
||||||
</a>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<form class="navbar-form navbar-left" role="search" ng-submit="$ctrl.gotoMailbox($ctrl.address)">
|
|
||||||
<input ng-model="$ctrl.address" type='text' class="form-control"/>
|
|
||||||
<button type="submit" class="btn btn-default">open</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<form ng-submit="$ctrl.gotoMailbox($ctrl.address)">
|
|
||||||
<div class="form-inline visible-xs-block">
|
|
||||||
<div class="form-group col-xs-4">
|
|
||||||
<span class="octicon-inbox"></span></a>
|
|
||||||
<a class="btn btn-default" ng-click="$ctrl.gotoRandomAddress()" role="button">
|
|
||||||
<span class="glyphicon glyphicon-random" aria-hidden="true"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="form-group col-xs-6">
|
|
||||||
<input ng-model="$ctrl.address" type='text' class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group col-xs-2">
|
|
||||||
<button type="submit" class="btn btn-default">open</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="container min-height" ng-switch="$ctrl.state">
|
|
||||||
|
|
||||||
<p ng-switch-when="home">
|
|
||||||
Use the buttons above to create a new inbox, or open a specific mailbox.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
<div ng-switch-when="list" ng-repeat="mail in $ctrl.mails | orderBy:'-date' track by $index"
|
|
||||||
class="email-table">
|
|
||||||
<mail mail="mail"></mail>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-switch-when="loading" class="waiting-screen">
|
|
||||||
<h1> </h1>
|
|
||||||
<p class="lead">Loading Mails</p>
|
|
||||||
<p><br/>
|
|
||||||
<img src="spinner.gif">
|
|
||||||
<br/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-switch-when="empty">
|
|
||||||
<div class="waiting-screen">
|
|
||||||
<h1>{{$ctrl.address}}</h1>
|
|
||||||
<p class="lead">Inbox is empty.</p>
|
|
||||||
<p><br/>
|
|
||||||
<img src="spinner.gif">
|
|
||||||
<br/></p>
|
|
||||||
<p class="lead">Emails to {{address}} will be automatically displayed on this page. </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
|
@ -1,14 +0,0 @@
|
||||||
import angular from 'angular';
|
|
||||||
import uiRouter from 'angular-ui-router';
|
|
||||||
|
|
||||||
import template from './inbox.html';
|
|
||||||
import controller from './inbox.controller';
|
|
||||||
import './inbox.scss';
|
|
||||||
|
|
||||||
export default angular.module('mailbox.inbox', [uiRouter])
|
|
||||||
.component('inbox', {
|
|
||||||
template, controller,
|
|
||||||
bindings: {
|
|
||||||
data: '<'
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,33 +0,0 @@
|
||||||
body {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer p {
|
|
||||||
margin-top: 50px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.email-table {
|
|
||||||
margin-top: 20px;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.waiting-screen {
|
|
||||||
padding: 40px 15px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.min-height {
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
background-color: #D9E2E9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.octicon-inbox {
|
|
||||||
display: inline-block;
|
|
||||||
width: 26px;
|
|
||||||
height: 23px;
|
|
||||||
background: url('octicon-inbox.png') no-repeat;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
describe('not tests', () => {
|
|
||||||
it('should run anyway', () => {
|
|
||||||
});
|
|
||||||
});
|
|
Binary file not shown.
Before Width: | Height: | Size: 7.0 KiB |
|
@ -1,8 +0,0 @@
|
||||||
class MailController {
|
|
||||||
/*@ngInject*/
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MailController;
|
|
|
@ -1,45 +0,0 @@
|
||||||
<section class="email">
|
|
||||||
<div class="row sticky-header" ec-stickyfill>
|
|
||||||
<div class="col-sm-12 email-summary">{{$ctrl.mail.subject}}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-12 email-headers">
|
|
||||||
<dl>
|
|
||||||
<dt>To:</dt>
|
|
||||||
<dd>{{$ctrl.mail.toString}}</dd>
|
|
||||||
<div ng-if="$ctrl.mail.cc" ng-repeat="(address,name) in $ctrl.mail.cc">
|
|
||||||
<dt>CC:</dt>
|
|
||||||
<dd>{{address}}</dd>
|
|
||||||
</div>
|
|
||||||
<dt>From:</dt>
|
|
||||||
<dd>{{$ctrl.mail.fromName}} <{{$ctrl.mail.fromAddress}}></dd>
|
|
||||||
<dt>Date:</dt>
|
|
||||||
<dd>{{$ctrl.mail.date}}</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row email-tabs">
|
|
||||||
<!-- if there is no html content, just show text: -->
|
|
||||||
<div ng-if="!$ctrl.mail.textHtml" ng-bind-html="$ctrl.mail.textPlain | nl2br | autolink" class="mail-conent">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- else show tabs with plain text and html:-->
|
|
||||||
<uib-tabset ng-if="$ctrl.mail.textHtml" active="1">
|
|
||||||
<uib-tab index="1">
|
|
||||||
<uib-tab-heading><i class="glyphicon glyphicon-align-left"></i> text</uib-tab-heading>
|
|
||||||
<div class="mail-conent" ng-bind-html="$ctrl.mail.textPlain | nl2br | autolink "></div>
|
|
||||||
</uib-tab>
|
|
||||||
|
|
||||||
<uib-tab index="2" select="htmlActive=true">
|
|
||||||
<uib-tab-heading><i class="glyphicon glyphicon-picture"></i> html</uib-tab-heading>
|
|
||||||
<!-- only load htl content when tab is activated. this avoids loading images from other servers.-->
|
|
||||||
<div class="mail-conent" ng-if="htmlActive" ng-bind-html="$ctrl.mail.textHtml"></div>
|
|
||||||
</uib-tab>
|
|
||||||
|
|
||||||
</uib-tabset>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
$email-border-color: #7C96AB;
|
|
||||||
$email-summary-background: white;
|
|
||||||
$email-header-background: white;
|
|
||||||
|
|
||||||
$tab-button-color: white;
|
|
||||||
$tab-button-color-inactive: #e0e0e0;
|
|
||||||
$tab-content-background: white;
|
|
||||||
|
|
||||||
.sticky-header {
|
|
||||||
top: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.email {
|
|
||||||
.email-summary {
|
|
||||||
font-weight: bold;
|
|
||||||
border-top: 5px solid $email-border-color;
|
|
||||||
border-bottom: 1px solid $email-border-color;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: $email-summary-background;
|
|
||||||
}
|
|
||||||
|
|
||||||
.email-headers {
|
|
||||||
background-color: $email-header-background;
|
|
||||||
|
|
||||||
dl {
|
|
||||||
padding: 0 0 4px;
|
|
||||||
color: black;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
dt {
|
|
||||||
float: left;
|
|
||||||
color: black;
|
|
||||||
margin: 0 3px 0 0;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.email-tabs {
|
|
||||||
background-color: $email-header-background;
|
|
||||||
border-bottom: 1px solid $email-border-color;
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mail-conent {
|
|
||||||
background-color: $tab-content-background;
|
|
||||||
padding: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab Button (disabled)*/
|
|
||||||
.nav-tabs > li.disabled > a:hover,
|
|
||||||
.nav-tabs > li.disabled > a:focus {
|
|
||||||
background-color: $tab-button-color-inactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab Button (incative)*/
|
|
||||||
.nav-tabs > li > a {
|
|
||||||
background-color: $tab-button-color-inactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab Button (active)*/
|
|
||||||
.nav-tabs > li.active > a,
|
|
||||||
.nav-tabs > li.active > a:hover,
|
|
||||||
.nav-tabs > li.active > a:focus {
|
|
||||||
background-color: $tab-button-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import angular from 'angular';
|
|
||||||
import uiRouter from 'angular-ui-router';
|
|
||||||
import Service from './service/mailbox.service'
|
|
||||||
import Inbox from './inbox/inbox';
|
|
||||||
import Mail from './mail/mail'
|
|
||||||
|
|
||||||
let module = angular.module('mailbox', [uiRouter, Inbox.name, Mail.name])
|
|
||||||
.config(/*@ngInject*/($stateProvider, $urlRouterProvider) => {
|
|
||||||
|
|
||||||
$urlRouterProvider.otherwise('/');
|
|
||||||
|
|
||||||
$stateProvider.state('inbox', {
|
|
||||||
url: '/:username',
|
|
||||||
component: 'inbox'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.service('mailboxService', Service);
|
|
||||||
|
|
||||||
export default module;
|
|
|
@ -1,18 +0,0 @@
|
||||||
class MailboxService {
|
|
||||||
/*@ngInject*/
|
|
||||||
constructor($http, $log, $state, $stateParams, config) {
|
|
||||||
this.$http = $http;
|
|
||||||
this.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadEmails(username) {
|
|
||||||
return this.$http.get(this.config.backend_url, {params: {username: username, action: "get"}})
|
|
||||||
.then(response=> {
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MailboxService;
|
|
17
src/util.js
Normal file
17
src/util.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import phonetic from "phonetic";
|
||||||
|
|
||||||
|
export function generateRandomUsername() {
|
||||||
|
let username = phonetic.generate({syllables: 3, phoneticSimplicity: 1});
|
||||||
|
if (Math.random() >= 0.5) {
|
||||||
|
username += this.getRandomInt(30, 99);
|
||||||
|
}
|
||||||
|
return username.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanUsername(username) {
|
||||||
|
return username.replace(/[@].*$/, '');
|
||||||
|
}
|
|
@ -1,90 +1,123 @@
|
||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var extend = require('node.extend');
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var browserSyncPlugin = require('browser-sync-webpack-plugin');
|
|
||||||
var ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
|
|
||||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
var webjsConfig = require('./shared.build.config');
|
var validate = require('webpack-validator');
|
||||||
var url = require('url');
|
var merge = require('webpack-merge');
|
||||||
var proxyMiddleware = require('proxy-middleware');
|
|
||||||
|
|
||||||
/**
|
var TARGET = process.env.npm_lifecycle_event;
|
||||||
* Gemeinsame Konfigurationsdatei fuer Webpack (der Teil, der fuer alle Umgebungen gleich ist)
|
|
||||||
* @type {} Webpack Konfiguration
|
// based on https://github.com/gaearon/react-hot-boilerplate
|
||||||
*/
|
const commonConfig = {
|
||||||
var commonConfig = {
|
|
||||||
context: path.resolve(__dirname, 'src'),
|
context: path.resolve(__dirname, 'src'),
|
||||||
// Einstiegspunkt fuer Webpack
|
entry: [
|
||||||
entry: {
|
'./app.js'
|
||||||
mailbox: './app.js'
|
],
|
||||||
// angular: 'angular',
|
|
||||||
// anguboot: 'angular-ui-bootstrap',
|
|
||||||
// angurouter: 'angular-ui-router',
|
|
||||||
// bootcss: 'bootstrap/dist/css/bootstrap.css',
|
|
||||||
// angusan: 'angular-sanitize',
|
|
||||||
// autolinker: 'autolinker',
|
|
||||||
// babelpolyfill: 'babel-polyfill',
|
|
||||||
// phonetic: 'phonetic'
|
|
||||||
},
|
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
filename: '[name]bundle.js'
|
filename: 'bundle_[hash].js'
|
||||||
},
|
|
||||||
// Modulkonfiguration fuer alle Dateitypen, welcher Loader soll verwendet werden
|
|
||||||
module: {
|
|
||||||
loaders: webjsConfig.webpackLoaders
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
fallback: path.join(__dirname, 'node_modules')
|
|
||||||
},
|
|
||||||
resolveLoader: {fallback: path.join(__dirname, 'node_modules')}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Production Konfigurationsdatei fuer Webpack (der Teil, der nur fuer den produktiven Build ist)
|
|
||||||
* @type {} Webpack Konfiguration
|
|
||||||
*/
|
|
||||||
var production = extend({}, commonConfig, {
|
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, 'target/build'),
|
|
||||||
filename: '[name]_[hash].js'
|
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new ngAnnotatePlugin({add: true}),
|
|
||||||
new webpack.optimize.DedupePlugin(),
|
|
||||||
new webpack.NoErrorsPlugin(),
|
new webpack.NoErrorsPlugin(),
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
|
||||||
minimize: true,
|
|
||||||
compress: {
|
|
||||||
warnings: true
|
|
||||||
},
|
|
||||||
sourceMap: false
|
|
||||||
}),
|
|
||||||
// add js files
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: './index.html'
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// development config
|
|
||||||
|
|
||||||
// forward requests (you may also have to change "backend_url" in app.js
|
|
||||||
var proxyOptions = url.parse('http://localhost:8080');
|
|
||||||
proxyOptions.route = '/backend.php';
|
|
||||||
|
|
||||||
var development = extend({}, commonConfig, {
|
|
||||||
plugins: [
|
|
||||||
new browserSyncPlugin({
|
|
||||||
proxy: 'localhost:3000',
|
|
||||||
middleware: proxyMiddleware(proxyOptions)
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: './index.html'
|
template: './index.html'
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
watch: true,
|
module: {
|
||||||
devtool: 'source-map'
|
loaders: [
|
||||||
});
|
{
|
||||||
|
test: /\.json$/, loader: 'json'
|
||||||
|
}, {
|
||||||
|
test: /\.html$/, loader: 'html'
|
||||||
|
}, {
|
||||||
|
test: /\.css$/, loader: 'style!css'
|
||||||
|
}, {
|
||||||
|
test: /\.scss$/, loader: 'style!css!sass'
|
||||||
|
}, {
|
||||||
|
test: /\.(jpe?g|png|gif|svg)$/i, loader: 'url'
|
||||||
|
}, {
|
||||||
|
test: /\.(woff|woff2)$/, loader: 'url?mimetype=application/font-woff'
|
||||||
|
}, {
|
||||||
|
test: /\.ttf$/, loader: 'url'
|
||||||
|
}, {
|
||||||
|
test: /\.eot$/, loader: 'url'
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var config;
|
||||||
|
switch (TARGET) {
|
||||||
|
case 'size':
|
||||||
|
case 'build':
|
||||||
|
config = merge(commonConfig, {
|
||||||
|
plugins: [
|
||||||
|
new webpack.optimize.DedupePlugin(),
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
minimize: true,
|
||||||
|
compress: {
|
||||||
|
warnings: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
DEVELOPMENT: JSON.stringify(false)
|
||||||
|
}),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
'NODE_ENV': JSON.stringify('production')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
],
|
||||||
|
// without react-hot in prod
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.js$/, exclude: [/node_modules/],
|
||||||
|
loader: 'babel',
|
||||||
|
query: {
|
||||||
|
// https://github.com/babel/babel-loader#options
|
||||||
|
cacheDirectory: true,
|
||||||
|
presets: ['es2015']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// develop
|
||||||
|
config = merge(commonConfig, {
|
||||||
|
devtool: 'eval',
|
||||||
|
plugins: [
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
DEVELOPMENT: JSON.stringify(true)
|
||||||
|
})
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
loaders: ['babel'],
|
||||||
|
include: path.join(__dirname, 'src')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
// replace entry instead of merge
|
||||||
|
config.entry = [
|
||||||
|
'webpack-dev-server/client?http://localhost:3000',
|
||||||
|
'webpack/hot/only-dev-server',
|
||||||
|
'./app.js'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TARGET === "size") {
|
||||||
|
// no validation with size target
|
||||||
|
module.exports = config;
|
||||||
|
} else {
|
||||||
|
module.exports = validate(config);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {production: production, development: development};
|
|
Loading…
Reference in New Issue
Block a user