diff --git a/.gitignore b/.gitignore
index f2515656..8dec467e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,7 +93,7 @@ local.properties
# PDT-specific
.buildpath
-# sbteclipse plugin
+# sbteclipse plugin
.target
# TeXlipse plugin
@@ -141,3 +141,8 @@ build/Release
# Destination folder
/www/
+
+.idea
+
+.idea/workspace.xml
+
diff --git a/gulpfile.js b/gulpfile.js
index fdd99d4f..229d989e 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -220,7 +220,16 @@ var gulp = require('gulp'),
spawn = require('child_process').spawn,
gutil = require('gulp-util');
-
+/**
+* Logs the error occured in the pipe without killing the gulp process
+* emits an end event to the corresponding stream
+* @function endErrorProcess
+* @param {Error} err
+*/
+function endErrorProcess(err){
+ console.log(err);
+ this.emit('end');
+}
/*================================================
= Report Errors to Console =
================================================*/
@@ -244,14 +253,16 @@ gulp.task('clean', function () {
path.join(config.dest, 'l10n'),
path.join(config.dest, 'app.manifest')
], { read: false })
- .pipe(rimraf());
+ .pipe(rimraf())
+ .on('error', endErrorProcess);
});
gulp.task('clean:manifest', function () {
return gulp.src([
path.join(config.dest, 'app.manifest')
], { read: false })
- .pipe(rimraf());
+ .pipe(rimraf())
+ .on('error', endErrorProcess);
});
@@ -279,7 +290,8 @@ gulp.task('connect', function() {
gulp.task('livereload', function () {
gulp.src(path.join(config.dest, '*.html'))
- .pipe(connect.reload());
+ .pipe(connect.reload())
+ .on('error', endErrorProcess);
});
@@ -295,10 +307,12 @@ gulp.task('images', function () {
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngcrush()]
- }));
+ }))
+ .on('error', endErrorProcess);
}
- return stream.pipe(gulp.dest(path.join(config.dest, 'images')));
+ return stream.pipe(gulp.dest(path.join(config.dest, 'images')))
+ .on('error', endErrorProcess);
});
@@ -308,7 +322,8 @@ gulp.task('images', function () {
gulp.task('fonts', function() {
return gulp.src(config.vendor.fonts)
- .pipe(gulp.dest(path.join(config.dest, 'fonts')));
+ .pipe(gulp.dest(path.join(config.dest, 'fonts')))
+ .on('error', endErrorProcess);
});
/*==================================
@@ -317,7 +332,8 @@ gulp.task('fonts', function() {
gulp.task('l10n', function() {
return gulp.src('src/l10n/**/*')
- .pipe(gulp.dest(path.join(config.dest, 'l10n')));
+ .pipe(gulp.dest(path.join(config.dest, 'l10n')))
+ .on('error', endErrorProcess);
});
@@ -358,7 +374,9 @@ function buildHtml (env) {
return gulp.src(['src/html/**/*.html'])
.pipe(replace('', inject.join('\n ')))
- .pipe(gulp.dest(config.dest));
+ .on('error', endErrorProcess)
+ .pipe(gulp.dest(config.dest))
+ .on('error', endErrorProcess);
}
gulp.task('html', function() {
@@ -377,10 +395,12 @@ gulp.task('html:production', function() {
gulp.task('sass', function () {
gulp.src('./src/sass/app.sass')
.pipe(sourcemaps.init())
+ .on('error', endErrorProcess)
.pipe(sass({
includePaths: [ path.resolve(__dirname, 'src/sass'), path.resolve(__dirname, 'bower_components'), path.resolve(__dirname, 'bower_components/bootstrap-sass/assets/stylesheets') ]
}).on('error', sass.logError))
.pipe(postcss([ autoprefixer({ browsers: ['last 2 versions', 'Android >= 4'] }) ]))
+ .on('error', endErrorProcess)
/* Currently not working with sourcemaps
.pipe(mobilizer('app.css', {
'app.css': {
@@ -394,11 +414,15 @@ gulp.task('sass', function () {
}))
*/
.pipe(gulpif(config.cssmin, cssmin()))
+ .on('error', endErrorProcess)
.pipe(rename({suffix: '.min'}))
+ .on('error', endErrorProcess)
.pipe(sourcemaps.write('.', {
sourceMappingURLPrefix: '/css/'
}))
- .pipe(gulp.dest(path.join(config.dest, 'css')));
+ .on('error', endErrorProcess)
+ .pipe(gulp.dest(path.join(config.dest, 'css')))
+ .on('error', endErrorProcess);
});
/*====================================================================
@@ -408,7 +432,9 @@ gulp.task('sass', function () {
gulp.task('jshint', function() {
return gulp.src('./src/js/**/*.js')
.pipe(jshint())
- .pipe(jshint.reporter('jshint-stylish'));
+ .on('error', endErrorProcess)
+ .pipe(jshint.reporter('jshint-stylish'))
+ .on('error', endErrorProcess);
});
@@ -422,44 +448,61 @@ gulp.task('js:app', function() {
return streamqueue({ objectMode: true },
// Vendor: angular, mobile-angular-ui, etc.
gulp.src(config.vendor.js)
- .pipe(sourcemaps.init()),
+ .pipe(sourcemaps.init())
+ .on('error', endErrorProcess),
// app.js is configured
gulp.src('./src/js/app.js')
.pipe(sourcemaps.init())
+ .on('error', endErrorProcess)
.pipe(replace('value(\'config\', {}). // inject:app:config',
'value(\'config\', ' + JSON.stringify(config.app) + ').'))
+ .on('error', endErrorProcess)
.pipe(babel({
presets: ['es2015']
- })),
+ }))
+ .on('error', endErrorProcess),
// rest of app logic
gulp.src(['./src/js/**/*.js', '!./src/js/app.js', '!./src/js/widgets.js'])
.pipe(sourcemaps.init())
+ .on('error', endErrorProcess)
.pipe(babel({
presets: ['es2015'],
plugins: ['transform-object-assign']
}))
- .pipe(ngFilesort()),
+ .on('error', endErrorProcess)
+ .pipe(ngFilesort())
+ .on('error', endErrorProcess),
// app templates
gulp.src(['src/templates/**/*.html']).pipe(templateCache({ module: 'Teem' }))
.pipe(sourcemaps.init())
+ .on('error', endErrorProcess)
.pipe(babel({
presets: ['es2015']
}))
+ .on('error', endErrorProcess)
)
.pipe(concat('app.js'))
+ .on('error', endErrorProcess)
.pipe(ngAnnotate())
+ .on('error', endErrorProcess)
.pipe(gulpif(config.uglify, uglify()))
+ .on('error', endErrorProcess)
.pipe(rename({suffix: '.min'}))
+ .on('error', endErrorProcess)
.pipe(sourcemaps.write('.', {
sourceMappingURLPrefix: '/js/'
}))
- .pipe(gulp.dest(path.join(config.dest, 'js')));
+ .on('error', endErrorProcess)
+ .pipe(gulp.dest(path.join(config.dest, 'js')))
+ .on('error', endErrorProcess);
});
gulp.task('js:widgets', function() {
return gulp.src('./src/js/widgets.js')
.pipe(uglify())
- .pipe(gulp.dest(path.join(config.dest, 'js')));
+ .on('error', endErrorProcess)
+ .pipe(gulp.dest(path.join(config.dest, 'js')))
+ .on('error', endErrorProcess);
});
@@ -481,7 +524,8 @@ gulp.task('cordova:sync:clean', function() {
return gulp.src([dest],
{ read: false })
- .pipe(rimraf());
+ .pipe(rimraf())
+ .on('error', endErrorProcess);
});
@@ -493,7 +537,8 @@ gulp.task('cordova:sync:copy', function() {
return gulp.src([ source + '{cordova.js,cordova_plugins.js,plugins/**/*}'])
- .pipe(gulp.dest(dest));
+ .pipe(gulp.dest(dest))
+ .on('error', endErrorProcess);
});
gulp.task('cordova:sync', function(cb) {
@@ -503,7 +548,8 @@ gulp.task('cordova:sync', function(cb) {
gulp.task('cordova', function() {
return gulp.src('src/vendor/cordova/**/*')
- .pipe(gulp.dest(path.join(config.dest, 'js/cordova')));
+ .pipe(gulp.dest(path.join(config.dest, 'js/cordova')))
+ .on('error', endErrorProcess);
});
@@ -530,7 +576,9 @@ function buildManifest (env) {
exclude: 'app.manifest',
hash: true
}))
- .pipe(gulp.dest(config.dest));
+ .on('error', endErrorProcess)
+ .pipe(gulp.dest(config.dest))
+ .on('error', endErrorProcess);
}
gulp.task('manifest', function(){
diff --git a/src/js/controllers/project.js b/src/js/controllers/project.js
index 7aa0c4f1..48dbd209 100644
--- a/src/js/controllers/project.js
+++ b/src/js/controllers/project.js
@@ -53,6 +53,10 @@ angular.module('Teem')
redirectTo: function(params) {
return '/teems/' + params.id + '?tab=' + params.tab;
}
+ })
+ .when('/trello/get/',{
+ controller: 'TrelloGetController',
+ template: '
Redirecting ....
'
});
}])
.controller('FetchProject', [
@@ -89,9 +93,9 @@ angular.module('Teem')
}])
.controller('ProjectCtrl', [
'SessionSvc', '$scope', '$rootScope', '$location', '$route', '$timeout', 'swellRT', '$filter',
- 'SharedState', 'ProjectsSvc', 'Loading', '$window', 'CommunitiesSvc', 'User', 'Selector', '$http', '$translate',
+ 'SharedState', 'ProjectsSvc', 'Loading', '$window', 'CommunitiesSvc', 'User', 'Selector', '$http', '$translate','trelloSvc',
function (SessionSvc, $scope, $rootScope, $location, $route, $timeout, swellRT, $filter,
- SharedState, ProjectsSvc, Loading, $window, CommunitiesSvc, User, Selector, $http, $translate) {
+ SharedState, ProjectsSvc, Loading, $window, CommunitiesSvc, User, Selector, $http, $translate, trelloSvc) {
// Prevent users from forging the form parameter
// and set the form order
@@ -255,7 +259,7 @@ angular.module('Teem')
$scope.pad.editing = false;
});
- $scope.$on('$routeChangeStart', function(event, next, current) {
+ $scope.$on('$routeChangeStart', function(event, next, current) {
if (current.params.tab !== undefined && $scope.project!== undefined) {
$scope.project.setTimestampAccess(current.params.tab);
}
@@ -281,12 +285,12 @@ angular.module('Teem')
var lastChange = $scope.project.lastChange(section);
var lastAccess;
- if ($scope.project.getTimestampAccess() &&
- $scope.project.getTimestampAccess()[section]) {
- lastAccess = new Date(($scope.project.getTimestampAccess()[section]).last);
- } else {
- lastAccess = new Date(0);
- }
+ if ($scope.project.getTimestampAccess() &&
+ $scope.project.getTimestampAccess()[section]) {
+ lastAccess = new Date(($scope.project.getTimestampAccess()[section]).last);
+ } else {
+ lastAccess = new Date(0);
+ }
return lastChange > lastAccess;
};
@@ -361,7 +365,7 @@ angular.module('Teem')
url
}).then(function (response) {
angular.forEach(response.data.geonames, function(v) {
- v.value = {
+ v.value = {
id: v.geonameId.toString(),
latitide: v.lat,
longitude: v.lng,
@@ -417,11 +421,22 @@ angular.module('Teem')
};
+ $scope.getToken = function () {
+ trelloSvc.getToken();
+ };
+
$scope.archiveProject = function() {
// TODO
};
}])
+ .controller('TrelloGetController', ['$location','ProjectsSvc','SessionSvc', function($location,ProjectsSvc,SessionSvc){
+ let token = $location.hash().split('=')[1];
+ localStorage.setItem('trelloTeemToken',token);
+ SessionSvc.onLoad(function(){
+ ProjectsSvc.updateTrello();
+ });
+ }])
.directive(
'hideTabs',
function (SharedState, $timeout) {
diff --git a/src/js/directives/pad.js b/src/js/directives/pad.js
index 8133d0f1..80d10821 100644
--- a/src/js/directives/pad.js
+++ b/src/js/directives/pad.js
@@ -1,13 +1,13 @@
'use strict';
/**
- * @ngdoc function
- * @name Teem.controller:ChatCtrl
- * @description
- * # Chat Ctrl
- * Show Pad for a given project
- */
-
+* @ngdoc function
+* @name Teem.controller:ChatCtrl
+* @description
+* # Chat Ctrl
+* Show Pad for a given project
+*/
+let timer,styleAppended = false;
angular.module('Teem')
.directive('pad', function() {
return {
@@ -16,237 +16,409 @@ angular.module('Teem')
$scope.editingDefault = attrs.editingDefault;
},
controller: [
- 'SessionSvc', '$rootScope', '$scope', '$route', '$location',
- '$timeout', 'SharedState', 'needWidget', '$element',
- function(SessionSvc, $rootScope, $scope, $route, $location,
- $timeout, SharedState, needWidget, $element) {
-
- var buttons = ['text_fields', 'format_bold', 'format_italic', 'format_strikethrough',
- 'format_align_left', 'format_align_center', 'format_align_right',
- 'format_list_bulleted', 'format_list_numbered'];
-
- var annotationMap = {
- 'text_fields': 'paragraph/header=h3',
- 'format_bold': 'style/fontWeight=bold',
- 'format_italic': 'style/fontStyle=italic',
- 'format_strikethrough': 'style/textDecoration=line-through',
- 'format_align_left': 'paragraph/textAlign=left',
- 'format_align_center': 'paragraph/textAlign=center',
- 'format_align_right': 'paragraph/textAlign=right',
- 'format_list_bulleted': 'paragraph/listStyleType=unordered',
- 'format_list_numbered': 'paragraph/listStyleType=decimal'
- };
-
- var annotations = {};
-
- function imgWidget(parentElement, before, state) {
- state = state || before;
-
- if (!(state in $scope.project.attachments) || !$scope.project.attachments[state].file) {
+ 'SessionSvc', '$rootScope', '$scope', '$route', '$location',
+ '$timeout', 'SharedState', 'needWidget', '$element','linkPreview',
+ function(SessionSvc, $rootScope, $scope, $route, $location,
+ $timeout, SharedState, needWidget, $element, linkPreview) {
+
+ var buttons = ['text_fields', 'format_bold', 'format_italic', 'format_strikethrough',
+ 'format_align_left', 'format_align_center', 'format_align_right',
+ 'format_list_bulleted', 'format_list_numbered'];
+
+ var annotationMap = {
+ 'text_fields': 'paragraph/header=h3',
+ 'format_bold': 'style/fontWeight=bold',
+ 'format_italic': 'style/fontStyle=italic',
+ 'format_strikethrough': 'style/textDecoration=line-through',
+ 'format_align_left': 'paragraph/textAlign=left',
+ 'format_align_center': 'paragraph/textAlign=center',
+ 'format_align_right': 'paragraph/textAlign=right',
+ 'format_list_bulleted': 'paragraph/listStyleType=unordered',
+ 'format_list_numbered': 'paragraph/listStyleType=decimal'
+ };
+
+ var annotations = {};
+
+ function openLinkPopover(event,range){
+ if(!styleAppended){
+ let pStyle = document.createElement('style');
+ pStyle.innerHTML = `
+ #popover{
+ width: 330px;
+ height: 270px;
+ margin: 0 5px;
+ border-radius: 6px;
+ }
+ div.popover-link-image{
+ width: 330px;
+ height: 190px;
+ margin: 5px auto;
+ }
+ div.popover-link-description{
+ width: 320px;
+ height: auto;
+ max-height: 40px;
+ margin: 0 auto;
+ overflow: auto;
+ word-wrap: break-all;
+ }
+ .popover-link-title{
+ word-wrap: break-all;
+ text-overflow: ellpsis;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ .popover-link-address{
+ color: #000;
+ margin-left: 5px;
+ overflow: auto;
+ word-wrap: break-all;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .popover-link-title{
+ margin-left: 5px;
+ }
+ #popover-container:after{
+ content: "";
+ position: absolute;
+ bottom: -25px;
+ left: 175px;
+ border-style: solid;
+ visibility: hidden;
+ width: 0;
+ z-index: 1;
+ }
+ #popover-container:before{
+ content: "";
+ position: absolute;
+ top: -11px;
+ left: -1px;
+ border-style: solid;
+ border-width: 0 10px 10px;
+ border-color: #F1F1F1 transparent;
+ display: block;
+ width: 0;
+ z-index: 0;
+ }`;
+ document.body.appendChild(pStyle);
+ styleAppended = true;
+ }
+ timer = $timeout(() => {
+ event.stopPropagation();
+ let div = document.createElement('div');
+ let btn = event.target;
+ console.dir(btn.offsetHeight);
+ let inHTML = `
+
+
+ `;
+ linkPreview.getMetaData(btn.href)
+ .then((meta) => {
+ console.log(meta);
+ if(!meta){
+ div.style.display = 'none';
return;
}
-
- // cannot use spinner template directly here
- parentElement.innerHTML = `
-
-
-
+ let urlImage = meta.image,
+ urlAuthor = meta.author,
+ urlTitle = meta.title,
+ urlDescription = meta.description;
+ if(urlImage && urlDescription){
+ inHTML = `
+
${urlTitle}
+
+
-
`;
-
- $scope.project.attachments[state].file.getUrl().then(url => {
- parentElement.innerHTML = `
`;
- });
- }
-
- $scope.padWidgets = {
- 'need': needWidget.getWidget($scope),
- 'img': {
- onInit: imgWidget,
- onChangeState: imgWidget
+
${urlDescription}
+
${btn.href}
+
`;
}
- };
-
- $scope.padAnnotations = {
- 'paragraph/header': {
- onAdd: function() {
- $scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
- $timeout();
- },
- onChange: function() {
- $scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
- $timeout();
- },
- onRemove: function() {
- $scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
- $timeout();
- }
- },
- 'link': {
- onEvent: function(range, event) {
- if (event.type === 'click') {
- event.stopPropagation();
- $scope.linkModal.open(range);
- }
+ else if(urlDescription && !urlImage){
+ div.style.height = '110px';
+ inHTML = `
+
+
${urlDescription}
+
${btn.href}
+
`;
+ }
+ else{
+ if(!urlTitle){
+ div.style.height = '110px';
+ inHTML = `
+
No Description provided ...
+
${btn.href}
+
`;
}
+ div.style.height = '110px';
+ inHTML = `
+
${urlTitle}
+
No Description provided ...
+
${btn.href}
+
`;
}
- };
-
- function updateAllButtons() {
- for (let btn of buttons) {
- let [key, val] = annotationMap[btn].split('=');
- $scope.buttons[btn] = (annotations && annotations[key] === val);
+ div.innerHTML = inHTML;
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ let clientRect = range.node.nextSibling ?
+ range.node.nextSibling.getBoundingClientRect() :
+ range.node.parentElement.getBoundingClientRect();
+ div.innerHTML = inHTML;
+ div.style.width = '345px';
+ div.style.height = '300px';
+ div.style.position = 'absolute';
+ div.style.border = '1px solid #F0F0F0';
+ div.style.top = clientRect.top + 35 + 'px';
+ div.style.left = clientRect.left + 'px';
+ div.style.zIndex = 3;
+ div.style.backgroundColor = '#F2F2F2';
+ div.style.paddingTop = '5px';
+ div.id = 'popover-container';
+ document.body.appendChild(div);
+ },700);
+ }
+
+ function closeLinkPopover(delay){
+ if(timer){
+ $timeout.cancel(timer);
+ timer = null;
+ $timeout(() => {
+ if(document.getElementById('popover-container')){
+ document.body.removeChild(document.getElementById('popover-container'));
}
- $timeout();
- }
+ }, delay);
+ }
+ }
- function disableAllButtons() {
- $scope.buttons = {};
- buttons.forEach(btn => $scope.buttons[btn] = false);
- $timeout();
+ function imgWidget(parentElement, before, state) {
+ state = state || before;
+
+ if (!(state in $scope.project.attachments) || !$scope.project.attachments[state].file) {
+ return;
}
- $scope.padCreate = function(editor) {
+ // cannot use spinner template directly here
+ parentElement.innerHTML = `
+
`;
+
+ $scope.project.attachments[state].file.getUrl().then(url => {
+ parentElement.innerHTML = `
`;
+ });
+ }
+
+ $scope.padWidgets = {
+ 'need': needWidget.getWidget($scope),
+ 'img': {
+ onInit: imgWidget,
+ onChangeState: imgWidget
+ }
+ };
- $scope.linkModal = {
- add: function(event) {
+ $scope.padAnnotations = {
+ 'paragraph/header': {
+ onAdd: function() {
+ $scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
+ $timeout();
+ },
+ onChange: function() {
+ $scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
+ $timeout();
+ },
+ onRemove: function() {
+ $scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
+ $timeout();
+ }
+ },
+ 'link': {
+ onEvent: function(range, event) {
+ if (event.type === 'click') {
event.stopPropagation();
- let range = editor.getSelection();
- if (range.text) {
- editor.setAnnotation('link', '');
- }
+ closeLinkPopover(0);
$scope.linkModal.open(range);
- },
- open: function(range) {
- let annotation = editor.getAnnotationInRange(range, 'link');
-
- $scope.linkModal.range = range;
- $scope.linkModal.annotation = annotation;
+ }
+ else if(event.type === 'mouseover'){
+ openLinkPopover(event,range);
console.log(range);
- let clientRect = range.node.nextSibling ?
- range.node.nextSibling.getBoundingClientRect() :
- range.node.parentElement.getBoundingClientRect();
- document.getElementById('link-modal').style.top = clientRect.top + 25 + 'px';
- document.getElementById('link-modal').style.left = clientRect.left + 'px';
-
- $scope.linkModal.text = range.text;
- $scope.linkModal.link = annotation ? annotation.value : '';
- $scope.linkModal.show = true;
-
- let emptyInput = !range.text ? 'text': 'link';
- let autofocus = document.querySelector('#link-modal [ng-model="linkModal.' + emptyInput + '"]');
- $timeout(() => autofocus && autofocus.focus());
- },
- change: function() {
- let range = editor.setText($scope.linkModal.range, $scope.linkModal.text);
- editor.setAnnotationInRange(range, 'link', $scope.linkModal.link);
- $scope.linkModal.show = false;
- $scope.linkModal.edit = false;
- },
- clear: function() {
- editor.clearAnnotationInRange($scope.linkModal.range, 'link');
- $scope.linkModal.show = false;
- $scope.linkModal.edit = false;
}
- };
-
- disableAllButtons();
+ else if(event.type === 'mouseout'){
+ closeLinkPopover(500);
+ }
+ }
+ }
+ };
- editor.onSelectionChanged(function(range) {
- annotations = range.annotations;
- updateAllButtons();
- });
+ function updateAllButtons() {
+ for (let btn of buttons) {
+ let [key, val] = annotationMap[btn].split('=');
+ $scope.buttons[btn] = (annotations && annotations[key] === val);
+ }
+ $timeout();
+ }
+
+ function disableAllButtons() {
+ $scope.buttons = {};
+ buttons.forEach(btn => $scope.buttons[btn] = false);
+ $timeout();
+ }
+
+ $scope.padCreate = function(editor) {
+
+ $scope.linkModal = {
+ add: function(event) {
+ event.stopPropagation();
+ let range = editor.getSelection();
+ if (range.text) {
+ editor.setAnnotation('link', '');
+ }
+ $scope.linkModal.open(range);
+ },
+ open: function(range) {
+ let annotation = editor.getAnnotationInRange(range, 'link');
+
+ $scope.linkModal.range = range;
+ $scope.linkModal.annotation = annotation;
+ console.log(range);
+ let clientRect = range.node.nextSibling ?
+ range.node.nextSibling.getBoundingClientRect() :
+ range.node.parentElement.getBoundingClientRect();
+ document.getElementById('link-modal').style.top = clientRect.top + 25 + 'px';
+ document.getElementById('link-modal').style.left = clientRect.left + 'px';
+
+ $scope.linkModal.text = range.text;
+ $scope.linkModal.link = annotation ? annotation.value : '';
+ $scope.linkModal.show = true;
+
+ let emptyInput = !range.text ? 'text': 'link';
+ let autofocus = document.querySelector('#link-modal [ng-model="linkModal.' + emptyInput + '"]');
+ $timeout(() => autofocus && autofocus.focus());
+ },
+ change: function() {
+ let range = editor.setText($scope.linkModal.range, $scope.linkModal.text);
+ editor.setAnnotationInRange(range, 'link', $scope.linkModal.link);
+ $scope.linkModal.show = false;
+ $scope.linkModal.edit = false;
+ },
+ clear: function() {
+ editor.clearAnnotationInRange($scope.linkModal.range, 'link');
+ $scope.linkModal.show = false;
+ $scope.linkModal.edit = false;
+ }
};
- $scope.padReady = function(editor) {
- // FIXME
- // SwellRT editor is created with .wave-editor-off
- // Should use .wave-editor-on when SwellRT editor callback is available
- // https://github.com/P2Pvalue/swellrt/issues/84
- var editorElement = angular.element($element.find('.swellrt-editor').children()[0]);
+ disableAllButtons();
- editorElement.on('focus', updateAllButtons);
- editorElement.on('blur', disableAllButtons);
+ editor.onSelectionChanged(function(range) {
+ annotations = range.annotations;
+ updateAllButtons();
+ });
+ };
- $scope.pad.outline = editor.getAnnotationSet('paragraph/header');
+ $scope.padReady = function(editor) {
+ // FIXME
+ // SwellRT editor is created with .wave-editor-off
+ // Should use .wave-editor-on when SwellRT editor callback is available
+ // https://github.com/P2Pvalue/swellrt/issues/84
+ var editorElement = angular.element($element.find('.swellrt-editor').children()[0]);
- $scope.annotate = function(btn) {
- let [key, val] = annotationMap[btn].split('=');
- let currentVal = annotations[key];
- if (currentVal === val) {
- val = null;
- }
+ editorElement.on('focus', updateAllButtons);
+ editorElement.on('blur', disableAllButtons);
- annotations[key] = val;
- editor.setAnnotation(key, val);
- editorElement.focus();
- };
+ $scope.pad.outline = editor.getAnnotationSet('paragraph/header');
- $scope.clearFormat = function() {
- editor.clearAnnotation('style');
- editorElement.focus();
- };
+ $scope.annotate = function(btn) {
+ let [key, val] = annotationMap[btn].split('=');
+ let currentVal = annotations[key];
+ if (currentVal === val) {
+ val = null;
+ }
- $scope.widget = function(type) {
- if (type === 'need') {
- needWidget.add(editor, $scope);
- }
- if (type === 'img') {
- if (arguments[1] === undefined) { // First step
- $scope.pad.selectingFile = true;
- $timeout(() => $scope.pad.selectingFile = false);
- } else { // Second step
- $scope.pad.selectingFile = false;
- var id = $scope.project.addAttachment(arguments[1]);
- editor.addWidget('img', id);
- }
- }
- };
-
- $scope.editOn = function () {
- if (editorElement.attr('class') === 'wave-editor-on') {
- $scope.pad.editing = true;
- SessionSvc.showSaving = true;
- SharedState.turnOn('hiddenTabs');
- $timeout();
- }
- };
-
- $scope.editOff = function () {
- if (editorElement.attr('class') === 'wave-editor-on') {
- $scope.pad.editing = $scope.editingDefault;
- SessionSvc.showSaving = false;
- SharedState.turnOff('hiddenTabs');
- $timeout();
+ annotations[key] = val;
+ editor.setAnnotation(key, val);
+ editorElement.focus();
+ };
+
+ $scope.clearFormat = function() {
+ editor.clearAnnotation('style');
+ editorElement.focus();
+ };
+
+ $scope.widget = function(type) {
+ if (type === 'need') {
+ needWidget.add(editor, $scope);
+ }
+ if (type === 'img') {
+ if (arguments[1] === undefined) { // First step
+ $scope.pad.selectingFile = true;
+ $timeout(() => $scope.pad.selectingFile = false);
+ } else { // Second step
+ $scope.pad.selectingFile = false;
+ var id = $scope.project.addAttachment(arguments[1]);
+ editor.addWidget('img', id);
}
- };
+ }
+ };
- if ($scope.editingDefault && $scope.project.isParticipant()) {
+ $scope.editOn = function () {
+ if (editorElement.attr('class') === 'wave-editor-on') {
$scope.pad.editing = true;
+ SessionSvc.showSaving = true;
+ SharedState.turnOn('hiddenTabs');
+ $timeout();
}
+ };
- // FIXME We should get the pad text directly from the editor, but
- // I couldn't find the proper way
- if ($scope.project.isParticipant() && $scope.project.pad.text() === '') {
- $scope.pad.emptyTip = true;
+ $scope.editOff = function () {
+ if (editorElement.attr('class') === 'wave-editor-on') {
+ $scope.pad.editing = $scope.editingDefault;
+ SessionSvc.showSaving = false;
+ SharedState.turnOff('hiddenTabs');
+ $timeout();
}
-
};
- $scope.$watchCollection(function() {
- return SessionSvc.status;
- }, function(current) {
- $scope.pad.saving = !current.sync;
- });
+ if ($scope.editingDefault && $scope.project.isParticipant()) {
+ $scope.pad.editing = true;
+ }
- $scope.closePadEmptyTip = function closePadEmptyTip() {
- $scope.pad.emptyTip = false;
- $timeout(() => {
- angular.element(document.querySelector('.wave-editor-on')).focus();
- });
- };
+ // FIXME We should get the pad text directly from the editor, but
+ // I couldn't find the proper way
+ if ($scope.project.isParticipant() && $scope.project.pad.text() === '') {
+ $scope.pad.emptyTip = true;
+ }
+
+ };
+
+ $scope.$watchCollection(function() {
+ return SessionSvc.status;
+ }, function(current) {
+ $scope.pad.saving = !current.sync;
+ });
+
+ $scope.closePadEmptyTip = function closePadEmptyTip() {
+ $scope.pad.emptyTip = false;
+ $timeout(() => {
+ angular.element(document.querySelector('.wave-editor-on')).focus();
+ });
+ };
}],
templateUrl: 'pad.html'
diff --git a/src/js/services/pad/linkPreview.js b/src/js/services/pad/linkPreview.js
new file mode 100644
index 00000000..1d7a0284
--- /dev/null
+++ b/src/js/services/pad/linkPreview.js
@@ -0,0 +1,37 @@
+(function() {
+ 'use strict';
+
+
+/**
+ * @module Teem
+ * @method linkPreview
+ * @param {String} url
+ * Returns the parsed meta data of the given link
+ */
+
+let linkPreviewFactory = angular.module('Teem');
+
+
+function linkPreview($http) {
+ const LINK_PREVIEW_SERVER_URL = 'http://localhost:9090/fetch';
+ function getMetaData(url){
+ //TODO: implement a check for the URL to be correct
+ if(!url){
+ return;
+ }
+ return $http.post(LINK_PREVIEW_SERVER_URL,{url})
+ .then((res) => {
+ return res.data;
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ }
+
+ return {
+ getMetaData
+ };
+}
+linkPreview.$inject = ['$http'];
+linkPreviewFactory.factory('linkPreview', linkPreview);
+})();
\ No newline at end of file
diff --git a/src/js/services/pad/need-widget.js b/src/js/services/pad/need-widget.js
index e75568fa..b6e10dff 100644
--- a/src/js/services/pad/need-widget.js
+++ b/src/js/services/pad/need-widget.js
@@ -58,7 +58,9 @@ angular.module('Teem')
editor.deleteText(selection);
}
- need = scope.project.addNeed(need);
+ need = scope.project.addNeedWithoutTrello(need);
+ //FIXME Doesn't work with the Trello integration, for now the ones created in pad cannot be added to Trello
+ // need = scope.project.addNeed(need);
// To generate need added event after all the info is available
diff --git a/src/js/services/projectsService.js b/src/js/services/projectsService.js
index bff4ab11..5ae77ee1 100644
--- a/src/js/services/projectsService.js
+++ b/src/js/services/projectsService.js
@@ -2,616 +2,677 @@
angular.module('Teem')
.factory('ProjectsSvc', [
- 'swellRT', '$q', '$timeout', 'base64', 'SessionSvc', 'SwellRTCommon', 'User',
- '$rootScope', 'Logo', 'Url', 'Participation',
- function(swellRT, $q, $timeout, base64, SessionSvc, SwellRTCommon, User,
- $rootScope, Logo, Url, Participation){
-
- // class that expose only read methods of the project object
- class ProjectReadOnly extends aggregation(Object, Logo, Url, Participation.ReadOnly) {
-
- // val is a readOnly object from a SwellRT query result
- constructor (val) {
- // calling "this" is not allowed before super()
- super();
-
- if (val) {
- for (var k in val.root){
- if (val.root.hasOwnProperty(k)){
- this[k] = val.root[k];
+ 'swellRT', '$q', '$timeout', 'base64', 'SessionSvc', 'SwellRTCommon', 'User',
+ '$rootScope', 'Logo', 'Url', 'Participation', 'trelloSvc', '$location',
+ function (swellRT, $q, $timeout, base64, SessionSvc, SwellRTCommon, User,
+ $rootScope, Logo, Url, Participation, trelloSvc, $location) {
+
+ // class that expose only read methods of the project object
+ class ProjectReadOnly extends aggregation(Object, Logo, Url, Participation.ReadOnly) {
+
+ // val is a readOnly object from a SwellRT query result
+ constructor(val) {
+ // calling "this" is not allowed before super()
+ super();
+
+ if (val) {
+ for (var k in val.root) {
+ if (val.root.hasOwnProperty(k)) {
+ this[k] = val.root[k];
+ }
}
- }
- this._participants = val.participants;
- if (val._id && val._id.$oid){
- this._creationDate =
- parseInt(val._id.$oid.slice(0, 8), 16) * 1000;
+ this._participants = val.participants;
+ if (val._id && val._id.$oid) {
+ this._creationDate =
+ parseInt(val._id.$oid.slice(0, 8), 16) * 1000;
} else {
this._creationDate = 0;
}
+ }
}
- }
- get pathPrefix () { return '/teems/'; }
-
- getTimestampAccess () {
- var access;
-
- if (this.lastAccesses === undefined) {
- this.lastAccesses = [];
+ get pathPrefix() {
+ return '/teems/';
}
- angular.forEach(this.lastAccesses, function(a) {
- if (a.user === User.currentId()) {
- access = a;
- }
- });
+ getTimestampAccess() {
+ var access;
- return access;
- }
+ if (this.lastAccesses === undefined) {
+ this.lastAccesses = [];
+ }
- // section in {'chat', 'pad', 'needs'}
- lastChange (section) {
- if (section === undefined) {
- //cast to Date
- return new Date(Math.max(
- this.lastChange('chat').getTime(),
- this.lastChange('pad').getTime(),
- this.lastChange('needs').getTime()
- ));
- } else {
- switch (section) {
-
- case 'chat':
- if (this.chat && this.chat.length > 0) {
- return new Date(this.chat[this.chat.length -1].time);
- } else {
- return new Date(0);
+ angular.forEach(this.lastAccesses, function (a) {
+ if (a.user === User.currentId()) {
+ access = a;
}
- break;
-
- case 'pad':
- return new Date(this.pad.lastmodtime) || new Date(0);
-
- case 'needs':
- var lastChange = new Date(0);
- angular.forEach(this.needs, function(n){
- lastChange = Math.max(
- lastChange,
- new Date(n.time||0),
- (function() {
- var lastComment = new Date(0);
- angular.forEach(n.comments, function(c){
- lastComment = Math.max(lastComment, new Date(c.time || 0));
- });
- return lastComment;
- }())
- );
- });
- // cast to Date
- return new Date(lastChange);
+ });
+
+ return access;
+ }
- default:
- return new Date(0);
+ // section in {'chat', 'pad', 'needs'}
+ lastChange(section) {
+ if (section === undefined) {
+ //cast to Date
+ return new Date(Math.max(
+ this.lastChange('chat').getTime(),
+ this.lastChange('pad').getTime(),
+ this.lastChange('needs').getTime()
+ ));
+ } else {
+ switch (section) {
+
+ case 'chat':
+ if (this.chat && this.chat.length > 0) {
+ return new Date(this.chat[this.chat.length - 1].time);
+ } else {
+ return new Date(0);
+ }
+ break;
+
+ case 'pad':
+ return new Date(this.pad.lastmodtime) || new Date(0);
+
+ case 'needs':
+ var lastChange = new Date(0);
+ angular.forEach(this.needs, function (n) {
+ lastChange = Math.max(
+ lastChange,
+ new Date(n.time || 0),
+ (function () {
+ var lastComment = new Date(0);
+ angular.forEach(n.comments, function (c) {
+ lastComment = Math.max(lastComment, new Date(c.time || 0));
+ });
+ return lastComment;
+ }())
+ );
+ });
+ // cast to Date
+ return new Date(lastChange);
+
+ default:
+ return new Date(0);
+ }
}
}
- }
- get creationDate(){
- return this._creationDate;
- }
+ get creationDate() {
+ return this._creationDate;
+ }
+
+ // section in {'chat', 'pad', 'needs'}
+ // pos in {'last','prev'}
+ lastAccess(section, pos) {
+ var access;
- // section in {'chat', 'pad', 'needs'}
- // pos in {'last','prev'}
- lastAccess (section, pos) {
- var access;
+ angular.forEach(this.lastAccesses || [], function (a) {
+ if (a.user === User.currentId()) {
+ access = a;
+ }
+ });
- angular.forEach(this.lastAccesses || [], function(a) {
- if (a.user === User.currentId()) {
- access = a;
+ if (!pos) {
+ pos = 'last';
}
- });
- if (!pos) {
- pos = 'last';
+ return (access && access[section] && access[section][pos] ? new Date(access[section][pos]) : new Date(0));
}
- return (access && access[section] && access[section][pos] ? new Date(access[section][pos]) : new Date(0));
- }
-
- newMessagesCount () {
- var access = this.lastAccess('chat');
+ newMessagesCount() {
+ var access = this.lastAccess('chat');
- if (this.chat.length > 0){
- var i = this.chat.length - 1;
+ if (this.chat.length > 0) {
+ var i = this.chat.length - 1;
- while (i > -1 && (new Date(this.chat[i].time) > access)) {
- i --;
+ while (i > -1 && (new Date(this.chat[i].time) > access)) {
+ i--;
+ }
+ return this.chat.length - 1 - i;
+ } else {
+ return 0;
}
- return this.chat.length - 1 - i;
- } else {
- return 0;
}
- }
- padEditionCount () {
- if (this.lastAccess('pad').getTime() < this.pad.lastmodtime) {
- return 1;
- } else {
- return 0;
+ padEditionCount() {
+ if (this.lastAccess('pad').getTime() < this.pad.lastmodtime) {
+ return 1;
+ } else {
+ return 0;
+ }
}
- }
- isFeatured () {
- return this.featured !== undefined && this.featured !== 'false';
- }
+ isFeatured() {
+ return this.featured !== undefined && this.featured !== 'false';
+ }
- featureDate () {
- if (this.isFeatured()){
- return parseInt(this.featured)||0;
- } else {
- return -1;
+ featureDate() {
+ if (this.isFeatured()) {
+ return parseInt(this.featured) || 0;
+ } else {
+ return -1;
+ }
}
- }
- isSupporter (userId = User.currentId()) {
- if (!userId) {
- return false;
+ isSupporter(userId = User.currentId()) {
+ if (!userId) {
+ return false;
+ }
+
+ return this.supporters.indexOf(userId) > -1;
}
- return this.supporters.indexOf(userId) > -1;
- }
+ needCompletedCount() {
+ var completed = 0;
- needCompletedCount () {
- var completed = 0;
+ angular.forEach(this.needs, function (need) {
+ if (need.completed === 'true') {
+ completed++;
+ }
+ });
- angular.forEach(this.needs, function(need) {
- if (need.completed === 'true') {
- completed++;
- }
- });
+ return completed;
+ }
- return completed;
- }
+ needIncompletedCount() {
+ return this.needCount() - this.needCompletedCount();
+ }
- needIncompletedCount () {
- return this.needCount() - this.needCompletedCount();
- }
+ needCount() {
+ if (this.needs === undefined) {
+ return 0;
+ }
- needCount () {
- if (this.needs === undefined) {
- return 0;
+ return this.needs.length;
}
- return this.needs.length;
- }
+ progressPercentage() {
+ var size = this.needCount();
- progressPercentage () {
- var size = this.needCount();
+ if (size === 0) {
+ return 0;
+ }
- if (size === 0) {
- return 0;
+ return Math.round(this.needCompletedCount() * 100 / size);
}
- return Math.round(this.needCompletedCount() * 100 / size);
- }
+ // Show at least 1%
+ progressPercentageNotZero() {
+ var value = this.progressPercentage();
- // Show at least 1%
- progressPercentageNotZero () {
- var value = this.progressPercentage();
+ if (value === 0 && this.needCount() > 0) {
+ return 1;
+ }
- if (value === 0 && this.needCount() > 0) {
- return 1;
+ return value;
}
- return value;
- }
+ progressType() {
+ var percentage = this.progressPercentage();
- progressType () {
- var percentage = this.progressPercentage();
+ if (percentage < 33) {
+ return 'danger';
+ } else if (percentage > 66) {
+ return 'success';
+ } else {
+ return 'warning';
+ }
+ }
- if (percentage < 33) {
- return 'danger';
- } else if (percentage > 66) {
- return 'success';
- } else {
- return 'warning';
+ isShareMode(mode) {
+ return mode === this.shareMode;
}
}
- isShareMode (mode) {
- return mode === this.shareMode;
- }
- }
+ class Project extends aggregation(ProjectReadOnly, Participation.ReadWrite, SessionSvc.SynchedModel) {
- class Project extends aggregation(ProjectReadOnly, Participation.ReadWrite, SessionSvc.SynchedModel) {
+ setShareMode(shareMode) {
+ this.shareMode = shareMode;
+ }
- setShareMode (shareMode) {
- this.shareMode = shareMode;
- }
+ /*
+ * Record when the user had her last and previous access to one project section
+ */
+ setTimestampAccess(section, overridePrev) {
+ if (!this.isParticipant()) {
+ return;
+ }
- /*
- * Record when the user had her last and previous access to one project section
- */
- setTimestampAccess (section, overridePrev) {
- if (! this.isParticipant()) {
- return;
- }
+ if (this.getTimestampAccess() === undefined) {
+ this.lastAccesses.push({user: User.currentId()});
+ }
+ var timestamp = this.getTimestampAccess()[section];
- if (this.getTimestampAccess() === undefined){
- this.lastAccesses.push({user: User.currentId()});
- }
- var timestamp = this.getTimestampAccess()[section];
+ if (timestamp === undefined) {
+ timestamp = {};
+ }
- if (timestamp === undefined) {
- timestamp = {};
- }
+ if (timestamp instanceof Date) {
+ timestamp = {
+ last: timestamp
+ };
+ }
- if (timestamp instanceof Date) {
- timestamp = {
- last: timestamp
- };
- }
+ if (!overridePrev) {
+ timestamp.prev = timestamp.last || (new Date()).toJSON();
+ } else {
+ timestamp.prev = (new Date()).toJSON();
+ }
- if(! overridePrev){
- timestamp.prev = timestamp.last || (new Date()).toJSON();
- } else {
- timestamp.prev = (new Date()).toJSON();
- }
+ timestamp.last = (new Date()).toJSON();
- timestamp.last = (new Date()).toJSON();
+ this.getTimestampAccess()[section] = timestamp;
+ }
- this.getTimestampAccess()[section] = timestamp;
- }
+ toggleSupport() {
- toggleSupport () {
+ var userId = User.currentId();
- var userId = User.currentId();
+ if (userId === undefined) {
+ return;
+ }
+ var index = this.supporters.indexOf(userId);
- if (userId === undefined) {
- return;
+ if (index > -1) {
+ this.supporters.splice(index, 1);
+ } else {
+ this.supporters.push(userId);
+ }
}
- var index = this.supporters.indexOf(userId);
- if (index > -1) {
- this.supporters.splice(index, 1);
- } else {
- this.supporters.push(userId);
+ addAttachment(file) {
+ if (!file) {
+ return;
+ }
+ if (typeof this.attachments === 'undefined') {
+ this.attachments = {};
+ }
+ var swellFile = new swellRT.FileObject(file);
+ var id = Math.floor((1 + Math.random()) * 0x100000000000000).toString(16);
+ this.attachments[id] = {
+ id,
+ file: swellFile,
+ who: User.currentId(),
+ time: (new Date()).toJSON()
+ };
+ return id;
}
- }
- addAttachment (file) {
- if (!file) {
- return;
- }
- if (typeof this.attachments === 'undefined') {
- this.attachments = {};
+ addChatMessage(text, file) {
+ if (file) {
+ file = new swellRT.FileObject(file);
+ }
+ this.chat.push({
+ text,
+ file,
+ who: User.currentId(),
+ time: (new Date()).toJSON()
+ });
+
+ this.setTimestampAccess('chat', true);
}
- var swellFile = new swellRT.FileObject(file);
- var id = Math.floor((1+Math.random())*0x100000000000000).toString(16);
- this.attachments[id] = {
- id,
- file: swellFile,
- who: User.currentId(),
- time: (new Date()).toJSON()
- };
- return id;
- }
- addChatMessage (text, file) {
- if (file) {
- file = new swellRT.FileObject(file);
+ findNeed(id) {
+ return this.needs.filter(function (need) {
+ return need._id === id;
+ })[0];
}
- this.chat.push({
- text,
- file,
- who: User.currentId(),
- time: (new Date()).toJSON()
- });
- this.setTimestampAccess('chat', true);
- }
+ addNeed(need) {
+ // Quick dirty hack until SwellRT provides ids for array elements
+ need._id = Math.random().toString().substring(2);
+ need.author = SessionSvc.users.current();
+ need.time = (new Date()).toJSON();
+ need.completed = 'false';
- findNeed (id) {
- return this.needs.filter(function (need) {
- return need._id === id;
- })[0];
- }
+ this.setTimestampAccess('needs', true);
- addNeed(need) {
+ if (need.text === '') {
+ return need;
+ }
+ if (this.trello) {
+ if (this.trello.boardId && this.trello.listId) {
+ trelloSvc.addNewCard(this.trello, need).then((cardData) => {
+ need.trelloId = cardData.id;
+ this.needs.push(need);
+ })
+ .catch(err => console.log(err));
+ }
+ }
+ else {
+ this.needs.push(need);
+ }
- // Quick dirty hack until SwellRT provides ids for array elements
- need._id = Math.random().toString().substring(2);
- need.author = SessionSvc.users.current();
- need.time = (new Date()).toJSON();
- need.completed = 'false';
+ return need;
+ }
- this.needs.push(need);
- this.setTimestampAccess('needs', true);
+ addNeedWithoutTrello(need) {
+ need._id = Math.random().toString().substring(2);
+ need.author = SessionSvc.users.current();
+ need.time = (new Date()).toJSON();
+ need.completed = 'false';
+ this.setTimestampAccess('needs', true);
+ this.needs.push(need);
- return need;
- }
+ return need;
+ }
- toggleNeedCompleted (need) {
- var newStatus;
+ toggleNeedCompleted(need) {
+ var newStatus;
- if (! this.isParticipant()) {
- return;
- }
+ if (!this.isParticipant()) {
+ return;
+ }
- newStatus = need.completed !== 'true';
+ newStatus = need.completed !== 'true';
- need.completed = newStatus.toString();
+ need.completed = newStatus.toString();
- if (newStatus) {
- need.completionDate = (new Date()).toJSON();
- } else {
- delete need.completionDate;
+ if (newStatus) {
+ need.completionDate = (new Date()).toJSON();
+ trelloSvc.archiveCard(this.trello, need).then(data => need.closed = true)
+ .catch(err => console.log(err));
+ } else {
+ trelloSvc.unarchiveCard(this.trello, need).then(data => {
+ need.closed = false;
+ })
+ .catch(err => console.log(err));
+ delete need.completionDate;
+ }
}
- }
- toggleFeatured () {
- if (! this.isParticipant()) {
- return;
- }
+ toggleFeatured() {
+ if (!this.isParticipant()) {
+ return;
+ }
- if (this.isFeatured()){
- this.featured = 'false';
- } else {
- this.featured = new Date().getTime().toString();
+ if (this.isFeatured()) {
+ this.featured = 'false';
+ } else {
+ this.featured = new Date().getTime().toString();
+ }
}
- }
- removeNeed (need) {
- var i = this.needs.indexOf(need);
+ removeNeed(need) {
+ var i = this.needs.indexOf(need);
- this.needs.splice(i,1);
- }
+ this.needs.splice(i, 1);
- addNeedComment (need, comment) {
- if (!need.comments){
- need.comments = [];
+ trelloSvc.archiveNeed(this.trello, need).then(data => need.removed = true)
+ .catch(err => console.log(err));
}
- need.comments.push({
- text: comment,
- time: (new Date()).toJSON(),
- author: User.currentId()
- });
- this.setTimestampAccess('needs', true);
- }
- delete () {
- this.type = 'deleted';
- this.communities = [];
- }
- }
+ addNeedComment(need, comment) {
+ if (!need.comments) {
+ need.comments = [];
+ }
+ if (this.trello) {
+ trelloSvc.addNewComment(this.trello, need, comment).then(data => console.log(data))
+ .catch(err => console.log(err));
+ }
+ need.comments.push({
+ text: comment,
+ time: (new Date()).toJSON(),
+ author: User.currentId()
+ });
+ this.setTimestampAccess('needs', true);
+ }
- // Service functions //
+ delete() {
+ this.type = 'deleted';
+ this.communities = [];
+ }
+ }
- var openedProjects = {};
+ // Service functions //
+ var openedProjects = {};
- /*
- * Build options for all query
- */
- function buildAllQuery(options) {
- var query = {
- _aggregate: [
- {
- $match: {
- 'root.type': 'project',
- 'root.title': {$ne: ''}
+ /*
+ * Build options for all query
+ */
+ function buildAllQuery(options) {
+ var query = {
+ _aggregate: [
+ {
+ $match: {
+ 'root.type': 'project',
+ 'root.title': {$ne: ''}
+ }
+ },
+ {
+ $sort: {'root.pad.lastmodtime': -1}
+ },
+ {
+ $skip: options.pagination.pageIndex * options.pagination.pageSize
+ },
+ {
+ $limit: options.pagination.pageSize
}
- },
- {
- $sort : {'root.pad.lastmodtime': -1 }
- },
- {
- $skip: options.pagination.pageIndex * options.pagination.pageSize
- },
- {
- $limit: options.pagination.pageSize
- }
- ]
- };
+ ]
+ };
- // all method only list public and user's projects
- query._aggregate[0].$match.$or = [
- // when anonymous user, Users.currentId is undefined
- { 'participants': User.currentId() || 'anonymous' },
- { 'root.shareMode': 'public' }
- ];
+ // all method only list public and user's projects
+ query._aggregate[0].$match.$or = [
+ // when anonymous user, Users.currentId is undefined
+ {'participants': User.currentId() || 'anonymous'},
+ {'root.shareMode': 'public'}
+ ];
- if (options.contributor) {
- query._aggregate[0].$match.participants = options.contributor;
- }
+ if (options.contributor) {
+ query._aggregate[0].$match.participants = options.contributor;
+ }
- if (options.community) {
- query._aggregate[0].$match['root.communities'] = options.community;
- }
+ if (options.community) {
+ query._aggregate[0].$match['root.communities'] = options.community;
+ }
- if (options.localId) {
- query._aggregate[0].$match['root.localId'] = options.localId;
- }
+ if (options.localId) {
+ query._aggregate[0].$match['root.localId'] = options.localId;
+ }
- if (options.shareMode) {
- query._aggregate[0].$match['root.shareMode'] = 'public';
- }
+ if (options.shareMode) {
+ query._aggregate[0].$match['root.shareMode'] = 'public';
+ }
- if (options.titleLike) {
- query._aggregate[0].$match['root.title'] = {
- $regex: options.titleLike,
- $options: 'i'
- };
- }
+ if (options.titleLike) {
+ query._aggregate[0].$match['root.title'] = {
+ $regex: options.titleLike,
+ $options: 'i'
+ };
+ }
- if (options.featured) {
- query._aggregate[0].$match['root.featured'] = {$exists: true, $ne: 'false'};
- query._aggregate[1].$sort = {'root.featured' : -1};
- }
+ if (options.featured) {
+ query._aggregate[0].$match['root.featured'] = {$exists: true, $ne: 'false'};
+ query._aggregate[1].$sort = {'root.featured': -1};
+ }
- if (options.latest) {
- query._aggregate[1].$sort = {'_id' : -1};
- }
+ if (options.latest) {
+ query._aggregate[1].$sort = {'_id': -1};
+ }
- if (options.projection){
- query._aggregate.push({
- $project: options.projection
- });
- }
+ if (options.projection) {
+ query._aggregate.push({
+ $project: options.projection
+ });
+ }
- return query;
- }
+ return query;
+ }
- /*
- * Find all the projects that meet some condition
- */
- function all(options) {
+ /*
+ * Find all the projects that meet some condition
+ */
+ function all(options) {
- if (!options.pagination) {
- options.pagination = {};
- }
+ if (!options.pagination) {
+ options.pagination = {};
+ }
- if (!options.pagination.pageSize) {
- options.pagination.pageSize = 12;
- }
+ if (!options.pagination.pageSize) {
+ options.pagination.pageSize = 12;
+ }
- if (!options.pagination.pageIndex) {
- options.pagination.pageIndex = 0;
- }
+ if (!options.pagination.pageIndex) {
+ options.pagination.pageIndex = 0;
+ }
- var projects = [],
- query = buildAllQuery(options);
+ var projects = [],
+ query = buildAllQuery(options);
- var nextPage = function () {
- // build a new options parameter for next page
- var nextPageOptions = options;
+ var nextPage = function () {
+ // build a new options parameter for next page
+ var nextPageOptions = options;
- nextPageOptions.pagination.pageIndex += 1;
+ nextPageOptions.pagination.pageIndex += 1;
- return all(nextPageOptions);
- };
+ return all(nextPageOptions);
+ };
- var projsPromise = $q(function(resolve, reject) {
- swellRT.query(query, function(result) {
+ var projsPromise = $q(function (resolve, reject) {
+ swellRT.query(query, function (result) {
- if (result.length === 0) {
- nextPage = undefined;
- }
+ if (result.length === 0) {
+ nextPage = undefined;
+ }
- angular.forEach(result.result, function(val){
- var v = new ProjectReadOnly(val);
- projects.push(v);
- });
+ angular.forEach(result.result, function (val) {
+ var v = new ProjectReadOnly(val);
+ projects.push(v);
+ });
- resolve(projects);
- },
- function(error){
- console.log(error);
+ resolve(projects);
+ },
+ function (error) {
+ console.log(error);
- reject([]);
+ reject([]);
+ });
});
- });
- projsPromise.next = nextPage;
+ projsPromise.next = nextPage;
- return projsPromise;
- }
+ return projsPromise;
+ }
- function contributing (options = {}) {
+ function contributing(options = {}) {
- if (!SessionSvc.users.loggedIn()) {
- return $q(function(resolve) {
- resolve([]);
- });
- }
+ if (!SessionSvc.users.loggedIn()) {
+ return $q(function (resolve) {
+ resolve([]);
+ });
+ }
- options.contributor = User.currentId();
+ options.contributor = User.currentId();
- return all(options);
- }
+ return all(options);
+ }
- function find(id) {
- var def = $q.defer();
+ function find(id) {
+ var def = $q.defer();
- if (!openedProjects[id]) {
- openedProjects[id] = def.promise;
- swellRT.openModel(id, function(model){
- $timeout(function(){
- var pr = swellRT.proxy(model, Project);
- def.resolve(pr);
+ if (!openedProjects[id]) {
+ openedProjects[id] = def.promise;
+ swellRT.openModel(id, function (model) {
+ $timeout(function () {
+ var pr = swellRT.proxy(model, Project);
+ def.resolve(pr);
+ });
+ }, function (error) {
+ console.log(error);
+ def.reject(error);
});
- }, function(error){
- console.log(error);
- def.reject(error);
- });
+ }
+ return openedProjects[id];
+ }
+
+ function findByUrlId(urlId) {
+ return find(base64.urldecode(urlId));
}
- return openedProjects[id];
- }
- function findByUrlId(urlId) {
- return find(base64.urldecode(urlId));
- }
+ function create(options, callback) {
+ var d = $q.defer();
- function create(options, callback) {
- var d = $q.defer();
+ swellRT.createModel(function (model) {
+ openedProjects[model.id()] = d.promise;
- swellRT.createModel(function(model){
- openedProjects[model.id()] = d.promise;
+ SwellRTCommon.makeModelPublic(model);
- SwellRTCommon.makeModelPublic(model);
+ var proxyProj;
- var proxyProj;
+ $timeout(function () {
+ proxyProj = swellRT.proxy(model, Project);
+ });
- $timeout(function(){
- proxyProj = swellRT.proxy(model, Project);
+ $timeout(function () {
+ proxyProj.type = 'project';
+ proxyProj.communities =
+ (options.communityId) ? [options.communityId] : [];
+ proxyProj.id = model.id();
+ proxyProj.title = '';
+ proxyProj.chat = [];
+ proxyProj.pad = new swellRT.TextObject();
+ proxyProj.attachments = {};
+ proxyProj.needs = [];
+ proxyProj.lastAccesses = [];
+ proxyProj.promoter = User.currentId();
+ proxyProj.supporters = [];
+ proxyProj.shareMode = 'public';
+ d.resolve(proxyProj);
+ });
});
- $timeout(function(){
- proxyProj.type = 'project';
- proxyProj.communities =
- (options.communityId) ? [ options.communityId ] : [];
- proxyProj.id = model.id();
- proxyProj.title = '';
- proxyProj.chat = [];
- proxyProj.pad = new swellRT.TextObject();
- proxyProj.attachments = {};
- proxyProj.needs = [];
- proxyProj.lastAccesses = [];
- proxyProj.promoter = User.currentId();
- proxyProj.supporters = [];
- proxyProj.shareMode = 'public';
- d.resolve(proxyProj);
+ d.promise.then(callback);
+
+ return d.promise;
+ }
+
+ function updateTrello() {
+ $location.path(localStorage.getItem('projectUrl'));
+ let locationSplit = $location.path().split('/');
+ let urlId = locationSplit[locationSplit.length - 1];
+ let id = base64.urldecode(urlId);
+ if (!localStorage.getItem('trelloTeemToken')) return;
+ find(id).then((model) => {
+ model.trello = {};
+ model.trello.token = localStorage.getItem('trelloTeemToken');
+ localStorage.removeItem('trelloTeemToken');
+ trelloSvc.createTrelloBoard(model).then((BoardData) => {
+ model.trello.boardId = BoardData.id;
+ trelloSvc.createNewList(model.trello).then((listData) => {
+ model.trello.listId = listData.id;
+ })
+ .catch(err => console.log(err));
+ })
+ .catch(err => console.error(err));
});
- });
-
- d.promise.then(callback);
-
- return d.promise;
- }
-
- var projectListProjection = {
- participants: 1,
- root: {
- title: 1,
- image: 1,
- id: 1,
- _urlId: 1,
- type: 1,
- featured: 1,
- communities: 1,
- pad: {
- lastmodtime: 1
- }
}
- };
-
- return {
- all,
- contributing,
- findByUrlId,
- find,
- create,
- projectListProjection
- };
- }]);
+
+ var projectListProjection = {
+ participants: 1,
+ root: {
+ title: 1,
+ image: 1,
+ id: 1,
+ _urlId: 1,
+ type: 1,
+ featured: 1,
+ communities: 1,
+ pad: {
+ lastmodtime: 1
+ }
+ }
+ };
+
+ return {
+ all,
+ contributing,
+ findByUrlId,
+ find,
+ create,
+ projectListProjection,
+ updateTrello
+ };
+ }]);
diff --git a/src/js/services/trelloService.js b/src/js/services/trelloService.js
new file mode 100644
index 00000000..45210e6b
--- /dev/null
+++ b/src/js/services/trelloService.js
@@ -0,0 +1,101 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('Teem')
+ .factory('trelloSvc', trelloSvc);
+ trelloSvc.$inject = ['$http', '$location', '$window', '$q'];
+ function trelloSvc($http, $location, $window, $q) {
+
+ function getToken() {
+ localStorage.setItem('projectUrl', $location.path());
+ const redirUrl = 'https://trello.com/1/authorize?expiration=never&name="Teem"&key=09e4aced60041e389dbb27b9accadd65&callback_method=fragment&scope=read%2Cwrite%2Caccount&return_url=http://localhost:8000/trello/get/';
+ $window.location.href = redirUrl;
+ }
+
+ function parseTokenFromUrl(urlString) {
+ console.log(urlString);
+ }
+
+ function createTrelloBoard(prObj) {
+ let deferred = $q.defer();
+ if (!prObj.trello.token) {
+ getToken();
+ deferred.resolve();
+ return deferred.promise;
+ }
+ console.log(prObj);
+ const BoardApiURL = `https://api.trello.com/1/boards/?name=${prObj.title}&key=09e4aced60041e389dbb27b9accadd65&token=${prObj.trello.token}`;
+ $http.post(BoardApiURL).then((result) => {
+ deferred.resolve(result.data);
+ })
+ .catch((err) => {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ }
+
+ function createNewList(trelloObj) {
+ let deferred = $q.defer();
+ if (!trelloObj.boardId) {
+ deferred.reject('No trello Board Id found!');
+ }
+ else {
+ const listAPIendPoint = `https://api.trello.com/1/lists?name=Teem&idBoard=${trelloObj.boardId}&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}`;
+ $http.post(listAPIendPoint)
+ .then((result) => {
+ deferred.resolve(result.data);
+ })
+ .catch(err => deferred.reject(err));
+ }
+ return deferred.promise;
+ }
+
+ function addNewCard(trelloObj, need) {
+ let deferred = $q.defer();
+ const newCardURL = `https://api.trello.com/1/cards?idList=${trelloObj.listId}&name=${need.text}&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}`;
+ $http.post(newCardURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.resolve(err));
+ return deferred.promise;
+ }
+
+ function archiveCard(trelloObj, need){
+ let deferred = $q.defer();
+ const archiveCardURL = `https://api.trello.com/1/cards/${need.trelloId}?closed=true&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}`;
+ $http.put(archiveCardURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.reject(err));
+ return deferred.promise;
+ }
+
+ function unarchiveCard(trelloObj, need){
+ let deferred = $q.defer();
+ const archiveCardURL = `https://api.trello.com/1/cards/${need.trelloId}?closed=false&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}`;
+ $http.put(archiveCardURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.reject(err));
+ return deferred.promise;
+ }
+
+ function addNewComment(trelloObj, need, comment){
+ let deferred = $q.defer();
+ const newCommentURL = `https://api.trello.com/1/cards/${need.trelloId}/actions/comments?key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}&text=${comment}`;
+ $http.post(newCommentURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.reject(err));
+ return deferred.promise;
+ }
+
+ return {
+ getToken: getToken,
+ parseTokenFromUrl: parseTokenFromUrl,
+ createTrelloBoard: createTrelloBoard,
+ createNewList: createNewList,
+ addNewCard: addNewCard,
+ archiveCard: archiveCard,
+ addNewComment: addNewComment,
+ unarchiveCard: unarchiveCard
+ };
+ }
+})();
diff --git a/src/templates/pad.html b/src/templates/pad.html
index db88fc30..83009723 100644
--- a/src/templates/pad.html
+++ b/src/templates/pad.html
@@ -66,3 +66,5 @@
+
+
diff --git a/src/templates/projects/project/desktop.html b/src/templates/projects/project/desktop.html
index 7f4d870f..cb5b60eb 100644
--- a/src/templates/projects/project/desktop.html
+++ b/src/templates/projects/project/desktop.html
@@ -54,8 +54,9 @@
+
+
need.nav.title
diff --git a/swellrt/docker-compose.yml b/swellrt/docker-compose.yml
index 32d8367f..65cb8dbb 100644
--- a/swellrt/docker-compose.yml
+++ b/swellrt/docker-compose.yml
@@ -15,6 +15,11 @@ services:
mongo:
image: mongo:latest
restart: always
+ teem-link-preview:
+ image: krshubham/teem-link-preview:latest
+ restart: always
+ ports:
+ - "0.0.0.0:9090:9090"