Skip to content

Instantly share code, notes, and snippets.

@alchen
Last active December 18, 2015 07:30
Show Gist options
  • Save alchen/3ca9d24bf26ba1b1bad2 to your computer and use it in GitHub Desktop.
Save alchen/3ca9d24bf26ba1b1bad2 to your computer and use it in GitHub Desktop.
Partial mithril.js implementation of timeline display for alchen/DTCP
'use strict';
var m = require('./mithril');
var twitterText = require('twitter-text');
var newTweet = {};
newTweet.Tweet = function Tweet(status) {
status = status || '';
this.status = m.prop(status);
this.formattedStatus = m.prop(twitterText.autoLink(status));
this.remainingLength = m.prop(
status ? 140 - twitterText.getTweetLength(status) : 140
);
};
newTweet.Tweet.prototype.setStatus = function setStatus(status) {
this.status(status);
this.formattedStatus(twitterText.autoLink(status));
this.remainingLength(
status ? 140 - twitterText.getTweetLength(status) : 140
);
};
newTweet.Tweet.prototype.isValidTweetText = function setStatus() {
return twitterText.isValidTweetText(this.status());
};
newTweet.vm = (function () {
var vm = {};
vm.init = function () {
//a slot to store the name of a new todo before it is created
vm.tweet = new newTweet.Tweet();
//adds a todo to the list, and clears the description field for user convenience
vm.format = function (status) {
newTweet.vm.tweet.setStatus(status);
};
vm.updateStatus = function () {
console.log('papa');
};
};
return vm;
}());
//the controller defines what part of the model is relevant for the current page
//in our case, there's only one view-model that handles everything
newTweet.controller = function() {
newTweet.vm.init();
};
//here's the view
newTweet.view = function() {
return m('html', [
m('head', [
m('title', 'testbed'),
m('link', {rel: 'stylesheet', href: 'css/normalize.css'}),
m('link', {rel: 'stylesheet', href: 'css/custom.css'}),
]),
m('body', [
m('div', {class: 'newtweet'}, [
m('section', {class: 'newtweettext'}, [
m('section', {
class: 'newrawtweet',
contenteditable: 'true',
autofocus: 'true',
oninput: m.withAttr('innerHTML', newTweet.vm.format)
}),
m('section', {
class: 'newformattedtweet',
innerHTML: newTweet.vm.tweet.formattedStatus()
})
]),
m('section', {class: 'newtweetmeta'}, [
m('span', {class: 'remaining'}, newTweet.vm.tweet.remainingLength()),
m('button', newTweet.vm.tweet.isValidTweetText() ? {
class: 'newtweetbutton activenewtweetbutton',
onclick: newTweet.vm.updateStatus
} : {
class: 'newtweetbutton'
}, 'Tweet')
])
])
])
]);
};
//initialize the application
m.mount(document, {controller: newTweet.controller, view: newTweet.view});
'use strict';
var _ = require('lodash');
var moment = require('moment');
var m = require('./lib/mithril');
var Tweet = require('./models/tweet');
var TweetComponent = require('./components/tweet');
var vm = (function () {
var vm = {};
vm.init = function () {
var tweets = require('./tweets.json');
vm.timeline = _.map(tweets, function (tweet) {
return new Tweet(tweet);
});
vm.now = m.prop(moment());
setInterval(function () {
vm.now(moment());
m.redraw();
}, 60 * 1000);
};
return vm;
}());
var controller = function() {
vm.init();
};
var view = function() {
return m('html', [
m('head', [
m('title', 'testbed'),
m('link', {rel: 'stylesheet', href: 'css/normalize.css'}),
m('link', {rel: 'stylesheet', href: 'css/iconic-glyphs.css'}),
m('link', {rel: 'stylesheet', href: 'css/custom.css'}),
]),
// m('body', vm.timeline)
m('body', [
m('ul', {class: 'tweets timeline home activetimeline'}, _.map(vm.timeline, function (tweet) {
return m.component(TweetComponent, {
tweet: tweet,
now: vm.now
});
})
)
])
]);
};
m.mount(document, {controller: controller, view: view});
'use strict';
// var ipc = require('ipc');
var _ = require('lodash');
var m = require('../lib/mithril');
var moment = require('moment');
var twitterText = require('twitter-text');
var Tweet = function (tweet) {
var options = {
usernameIncludeSymbol: true
};
// Accommodate different type of events
this.type = m.prop('tweet');
// Tweet status
this.id = m.prop(tweet.id_str);
this.rawStatus = m.prop(tweet.text);
this.formattedStatus = m.prop(
tweet.entities ? twitterText.autoLinkWithJSON(tweet.text, tweet.entities, options)
: twitterText.autoLink(tweet.text, options)
);
this.inReplyTo = m.prop(tweet.in_reply_to_status_id_str);
this.isRetweeted = m.prop(tweet.retweeted);
this.isFavorited = m.prop(tweet.favorited);
this.createdAt = m.prop(moment(new Date(tweet.created_at)));
// User info
this.screenName = m.prop(tweet.user.screen_name);
this.name = m.prop(tweet.user.name);
this.userId = m.prop(tweet.user.id_str);
this.icon = m.prop(tweet.user.profile_image_url_https);
this.protected = m.prop(tweet.user.protected);
this.mentions = m.prop(tweet.entities ?
_.map(tweet.entities.user_mentions, function (k) {
return k.screen_name;
}) : []
);
this.media = m.prop(tweet.extended_entities ?
_.map(tweet.extended_entities.media, function (k) {
return k.media_url_https;
}) : []
);
if (tweet.retweeted_status) {
this.retweet = m.prop(new Tweet(tweet.retweeted_status));
} else {
this.retweet = m.prop();
}
// Quote info
if (tweet.quoted_status) {
this.quote = m.prop(new Tweet(tweet.quoted_status));
} else {
this.quote = m.prop();
}
};
Tweet.prototype.showName = function () {
var self = this.retweet() || this;
return self.name();
};
Tweet.prototype.showScreenName = function () {
var self = this.retweet() || this;
return self.screenName();
};
Tweet.prototype.showStatus = function () {
var self = this.retweet() || this;
return self.formattedStatus();
};
Tweet.prototype.isUserProtected = function () {
var self = this.retweet() || this.quote() || this;
return self.protected();
};
Tweet.prototype.reply = function (exclude) {
var self = this.retweet() || this;
var mentions = _.filter(self.mentions(), function (k) {
return k !== exclude;
});
mentions.unshift(self.screenName);
// ipc.send('reply', self.inReplyTo(), mentions);
};
module.exports = Tweet;
'use strict';
var _ = require('lodash');
var moment = require('moment');
var m = require('../lib/mithril');
function displayNone() {
this.style.visibility = 'hidden';
}
function timeFrom(time, now) {
var duration = moment.duration(now.diff(moment(new Date(time))));
var sign = null;
if ((sign = duration.as('second')) <= 5) {
return 'now';
} else if (sign < 60) {
return Math.round(sign) + 's';
} else if ((sign = duration.as('minute')) < 60) {
return Math.round(sign) + 'm';
} else if ((sign = duration.as('hour')) < 24) {
return Math.round(sign) + 'h';
} else if ((sign = duration.as('day')) <= 365) {
return Math.round(sign) + 'd';
} else {
sign = duration.as('year');
return Math.round(sign) + 'y';
}
}
function extendedEntities(media) {
var length = media.length;
var width = 'width: calc(100% / ' + length + ');';
return m('section', {class: 'tweetmedia'}, [
m('ul', {class: 'tweetimagelist'}, _.map(media, function (mediaUrl) {
return m('li', {class: 'tweetimagebox', style: width}, [
m('a', {
class: 'tweetimagelink',
target: '_blank',
style: 'background-image:url(\'' + mediaUrl + ':small\');',
href: mediaUrl
}, mediaUrl)
]);
}))
]);
}
var TweetComponent = {
controller: function (data) {
return data;
},
view: function (ctrl) {
var tweet = ctrl.tweet;
return m('li', {class: 'tweet'}, [
m('section', {class: 'tweetleft'}, [
m('img', {
class: 'tweeticon',
src: tweet.icon(),
onerror: displayNone
})
]),
m('section', {class: 'tweetright'}, [
m('section', {class: 'tweetmeta'}, [
m('section', {class: 'tweetmetaleft'}, [
m('span', {class: 'name'}, tweet.showName()),
m.trust('&nbsp;'),
m('span', {class: 'screenname'}, tweet.showScreenName())
]),
m('section', {class: 'tweetmetaright'}, [
m('span', {class: 'tweettime'}, timeFrom(tweet.createdAt(), ctrl.now())),
m('ul', {class: 'tweetactions'}, tweet.isUserProtected() ? [
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'share'
})
])
]),
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton disabledbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'loop-circular'
})
])
]),
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton disabledbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'double-quote-serif-left'
})
])
]),
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'star'
})
])
])
] : [
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'share'
})
])
]),
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'loop-circular'
})
])
]),
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'double-quote-serif-left'
})
])
]),
m('li', {class: 'tweetaction'}, [
m('button', {class: 'tweetbutton'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'star'
})
])
])
])
])
]),
m('section', {class: 'tweettext'}, m.trust(tweet.showStatus())),
tweet.retweet() ? m('section', {class: 'tweetretweet'}, [
m('span', {
class: 'iconic tweetbuttonicon',
'data-glyph': 'loop-square'
}),
m('span', {class: 'retweetname'}, tweet.name())
]) : '',
tweet.media().length ? extendedEntities(tweet.media()) : '',
tweet.quote() ? m('section', {class: 'quotedtweet'}, [
m('section', {class: 'quotedmeta'}, [
m('span', {class: 'name'}, tweet.quote().showName()),
m.trust('&nbsp;'),
m('span', {class: 'screenname'}, tweet.quote().showScreenName())
]),
m('section', {class: 'quotedtext'}, m.trust(tweet.quote().showStatus())),
tweet.quote().media().length ? extendedEntities(tweet.quote().media()) : ''
]) : ''
]),
]);
}
};
module.exports = TweetComponent;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment