Commit 1324c8fc authored by Alexei's avatar Alexei

Clean up files

The code style has been changed to fit Electron's coding style.

Files were moved to a better structure.

Added a method to load the public timeline.
parent 72582d5e
......@@ -5,17 +5,13 @@
"name": "Launch",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}\\main.js",
"program": "${workspaceRoot}/src/app/main.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",
"runtimeExecutable": null,
"runtimeArgs": [
"--nolazy"
],
"env": {
"NODE_ENV": "development"
},
"runtimeExecutable": "${workspaceRoot}/node_modules/electron-prebuilt/dist/electron.exe",
"runtimeArgs": [],
"env": { },
"externalConsole": false,
"sourceMaps": false,
"outDir": null
......
module.exports = {
VERIFY_CREDENTIALS: 'api/account/verify_credentials.json',
OAUTH_REQUEST_TOKEN: 'api/oauth/request_token',
OAUTH_AUTHORIZE: 'api/oauth/authorize',
OAUTH_ACCESS_TOKEN: 'api/oauth/access_token',
};
\ No newline at end of file
module.exports = {
RSD: '/rsd.xml',
VERIFY_CREDENTIALS: '/account/verify_credentials.json',
OAUTH_REQUEST_TOKEN: '/oauth/request_token',
OAUTH_AUTHORIZE: '/oauth/authorize',
OAUTH_ACCESS_TOKEN: '/oauth/access_token',
STATUSNET_CONFIG: '/statusnet/config.json',
STATUSNET_VERSION: '/statusnet/version.json'
};
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel='stylesheet' href='css/main.css' type='text/css'>
<title>Authorize application - qvitter.js</title>
</head>
<body>
<script>
'use strict';
const ipc = require('electron').ipcRenderer;
var element = document.createElement('input');
element.type = 'text';
element.id = 'pin-input';
element.className = 'pin-input';
element.name = 'pin';
element.maxLength = 7;
element.style.width = '56px';
document.body.appendChild(element);
element = document.createElement('input');
element.type = 'submit';
element.value = 'Submit';
element.onclick = function() {
ipc.send('verifier-ping', document.querySelector('.pin-input').value);
console.log("sending data");
};
document.body.appendChild(element);
</script>
</body>
</html>
\ No newline at end of file
body {
}
.container {
max-width: 960px;
width: 100%;
}
nav {
width: 100%;
}
#content {
clear: both;
margin: 0;
padding: 0 0 10px 0;
}
.tweet .noticeAuthor {
padding-left: 5px;
}
.tweet .notice {
padding-left: 55px;
}
.notice-card-wide .mdl-card {
width: 512px;
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type='text/javascript' src='index.js'></script>
<link rel='stylesheet' href='css/material-icons/material-icons.css'>
<link rel='stylesheet' href='css/mdl/material.min.css'>
<link rel='stylesheet' href='css/main.css' type='text/css'>
<script src='../node_modules/material-design-lite/material.min.js'></script>
<title>QvitterJS</title>
</head>
<body>
<div class="container">
<div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
<header>
<nav>
<div class="mdl-tabs__tab-bar">
<a href="#home-timeline" class="mdl-tabs__tab is-active">Home</a>
<a href="#public-timeline" class="mdl-tabs__tab">Public</a>
<a href="#mentions-timeline" class="mdl-tabs__tab">Mentions</a>
</div>
</nav>
</header>
<section id="content">
<div id="notices" class="notices">
<div class="mdl-tabs__panel is-active" id="home-timeline">
<div class="notice-card-wide mdl-card mdl-shadow--2dp">
<div class="mdl-card__title">
<i class="material-icons mdl-list__item-avatar">person</i>
<h2 class="mdl-card__title-text">
<h2 class="mdl-card__subtitle-text">Alexei Ivanov @alexivanov</h2>
</h2>
</div>
<div class="mdl-card__supporting-text">
@hannes2peer what's the term "quip"? I was checking the translation files today and saw that you have changed from "notices" to "quips"
</div>
<div class="mdl-card__actions mdl-card--border">
<a href="(URL or function)">Related Action</a>
</div>
</div>
</div>
<div class="mdl-tabs__panel" id="public-timeline"><p>Lorem ipsum dolor sit amet.</p><p>Lorem ipsum dolor sit amet.</p></div>
<div class="mdl-tabs__panel" id="mentions-timeline"><p>Lorem ipsum dolor sit amet.</p></div>
</div>
</section>
</div>
</div>
</body>
</html>
\ No newline at end of file
'use strict';
const remote = require('electron').remote;
const BrowserWindow = remote.BrowserWindow;
const ipcRenderer = require('electron').ipcRenderer;
var fs = require('fs'),
xml2js = require('xml2js'),
$ = require('jquery'),
parseString = require('xml2js').parseString,
OAuth = require('oauth'),
methods = require('./api-methods');
var access_token = {
public: '',
secret: '',
};
var allowed = false;
var authenticated = false;
var GSInstance = {
url: '',
fancyLink: false,
language: '',
email: '',
closed: false,
inviteOnly: false,
private: false,
textLimit: 0,
ssl: '',
};
var oauth = new OAuth.OAuth(
'https://quitter.no/api/oauth/request_token',
'https://quitter.no/api/oauth/access_token',
'e16cc7fd32c52ea10faedcbdb789f55b',
'fb69e34abb5828111b4e2e6d0ace7fed',
'1.0',
'oob',
'HMAC-SHA1'
);
var verifier = null;
function getRsd(callback) {
$.get('https://quitter.no/rsd.xml', function (data) {
parseString(data, function (e, r) {
var apis = r.rsd.service[0].apis;
for (var i = 0; i < apis.length; i++) {
var api = apis[i].api[i]['$'];
if (api.name === "Twitter") {
GSInstance.apiUrl = api.apiLink;
}
}
});
}, "text").done(function () {
getServerConfig(callback);
});
}
ipcRenderer.on('verifier-pong', function (event, data) {
verifier = data;
if (authorizeWindow !== null)
authorizeWindow.close();
});
$(function () {
ipcRenderer.send('did-finish-load');
});
ipcRenderer.on('needs-auth', function (event, data) {
oauth.getOAuthRequestToken(handleRequestToken);
});
ipcRenderer.on('token', function (event, data) {
access_token = data;
getRsd(verify_credentials);
});
var authorizeWindow = null;
function get(url, callback) {
if (allowed && typeof(callback) === 'function')
if (url.endsWith(methods.VERIFY_CREDENTIALS) || authenticated) {
oauth.get(url, window.atob(access_token.public), window.atob(access_token.secret), callback);
return true;
}
return false;
}
function post(url, callback, data) {
if (allowed && authenticated && typeof(data) === 'object' && typeof(callback) === 'function') {
oauth.post(url, window.atob(access_token.public), window.atob(access_token.secret), data, callback);
return true;
}
return false;
}
function combine_urls(a, b) {
if (typeof(a) === 'string' && typeof(b) === 'string')
return a + b;
return null;
}
function verify_credentials() {
var result = get(combine_urls(GSInstance.apiUrl, methods.VERIFY_CREDENTIALS), function(a, response, req) {
if (req.statusCode == 200 && req.statusMessage == 'OK') {
authenticated = true;
console.log("Authenthicated!");
}
});
}
function handleRequestToken(err, token, token_secret, parsedQueryString) {
if (err === null) {
if (parsedQueryString.oauth_callback_confirmed === 'true') {
authorizeWindow = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true
});
authorizeWindow.loadURL('https://quitter.no/api/oauth/authorize?oauth_token=' + token);
authorizeWindow.on('closed', function () {
authorizeWindow = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true
});
authorizeWindow.loadURL('file://' + __dirname + '/authorize.html');
authorizeWindow.webContents.openDevTools();
authorizeWindow.on('closed', function () {
authorizeWindow = null;
if (verifier !== null)
oauth.getOAuthAccessToken(token, token_secret, verifier, handleAccessToken);
});
});
}
}
}
function handleAccessToken(err, token, token_secret, parsedString) {
if (err === null) {
access_token.public = window.btoa(token);
access_token.secret = window.btoa(token_secret);
ipcRenderer.send('save-token', access_token);
verify_credentials();
}
}
function changeTab(tab) {
switch (tab) {
case 'public':
loadPublicTimeline();
break;
case 'home':
loadHomeTimeline();
break;
case 'mentions':
loadMentions();
break;
}
}
function getServerConfig(callback) {
$.getJSON(GSInstance.apiUrl + 'statusnet/config.json', function (data) {
var site = data.site;
GSInstance.fancyLink = Boolean(site.fancy);
GSInstance.language = site.language;
GSInstance.email = site.email;
GSInstance.closed = Boolean(site.closed);
GSInstance.inviteOnly = Boolean(site.inviteonly);
GSInstance.private = Boolean(site.private);
GSInstance.textLimit = site.textlimit;
GSInstance.ssl = site.ssl;
}).done(function () {
allowed = true;
callback.call();
});
}
function loadPublicTimeline() {
var notices = document.querySelector('#notices');
while (notices.firstChild) {
notices.removeChild(notices.firstChild);
}
var tabs = document.querySelector('.tabs');
tabs.children[0].id = '';
tabs.children[1].id = 'selected';
tabs.children[2].id = '';
$.getJSON(GSInstance.apiUrl + 'statuses/public_timeline.json', function (data) {
for (var i = 0; i < data.length; i++) {
var user = data[i].user;
var tweet = document.createElement('div');
tweet.id = 'tweet';
tweet.className = 'tweet';
var author = document.createElement('span');
author.id = 'author';
author.className = 'noticeAuthor';
var authorLink = document.createElement('a');
authorLink.text = user.name + ' @' + user.screen_name;
authorLink.href = user['statusnet:profile_url'];
authorLink.onclick = function () {
window.open(authorLink.href, authorLink.text);
};
author.appendChild(authorLink);
var avatarImage = document.createElement('img');
avatarImage.height = 48;
avatarImage.width = 48;
avatarImage.src = user.profile_image_url_https;
avatarImage.align = 'left';
avatarImage.className = 'authorAvatar';
var notice = document.createElement('p');
notice.id = 'notice';
notice.innerHTML = data[i].statusnet_html;
notice.className = 'notice';
tweet.appendChild(author);
tweet.appendChild(avatarImage);
tweet.appendChild(notice);
document.getElementById('notices').appendChild(tweet);
document.getElementById('notices').appendChild(document.createElement('br'));
}
});
}
function loadHomeTimeline() {
var notices = document.querySelector('#notices');
while (notices.firstChild) {
notices.removeChild(notices.firstChild);
}
var tabs = document.querySelector('.tabs');
tabs.children[0].id = 'selected';
tabs.children[1].id = '';
tabs.children[2].id = '';
}
function loadMentions() {
var notices = document.querySelector('#notices');
while (notices.firstChild) {
notices.removeChild(notices.firstChild);
}
get(GSInstance.apiUrl + 'statuses/mentions.json?include_rts=true', function(a, response, req) {
var data = JSON.parse(response);
console.log(data);
if (req.statusCode == 200 && req.statusMessage == 'OK') {
for (var i = 0; i < data.length; i++) {
var user = data[i].user;
var tweet = document.createElement('div');
tweet.id = 'tweet';
tweet.className = 'tweet';
var author = document.createElement('span');
author.id = 'author';
author.className = 'noticeAuthor';
var authorLink = document.createElement('a');
authorLink.text = user.name + ' @' + user.screen_name;
authorLink.href = user['statusnet:profile_url'];
authorLink.onclick = function () {
window.open(authorLink.href, authorLink.text);
};
author.appendChild(authorLink);
var avatarImage = document.createElement('img');
avatarImage.height = 48;
avatarImage.width = 48;
avatarImage.src = user.profile_image_url_https;
avatarImage.align = 'left';
avatarImage.className = 'authorAvatar';
var notice = document.createElement('p');
notice.id = 'notice';
notice.innerHTML = data[i].statusnet_html;
notice.className = 'notice';
tweet.appendChild(author);
tweet.appendChild(avatarImage);
tweet.appendChild(notice);
notices.appendChild(tweet);
notices.appendChild(document.createElement('br'));
}
}
});
var tabs = document.querySelector('.tabs');
tabs.children[0].id = '';
tabs.children[1].id = '';
tabs.children[2].id = 'selected';
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
\ No newline at end of file
......@@ -2,17 +2,15 @@
"name": "qvitter-js",
"version": "0.0.1",
"description": "GNUsocial client written in Javascript",
"main": "main.js",
"main": "src/app/main.js",
"scripts": {
"start": "electron ."
"start": "electron src/app/main.js"
},
"author": "Alexei Ivanov",
"license": "GPL-3.0",
"devDependencies": {
"electron-json-storage": "^2.0.0",
"electron-prebuilt": "^0.36.7",
"electron-settings": "^1.0.1",
"getmdl-select": "^1.0.4",
"electron-prebuilt": "^0.37.2",
"jquery": "^2.2.0",
"material-design-lite": "^1.1.1",
"oauth": "^0.9.14",
......
body {
background-color: #e0e0e0;
}
.quip-card {
margin: 0 auto;
width: 350px;
}
.quip-card .mdl-card__title {
display: inline-flex;
}
.quip-card .mdl-card__title-text {
font-size: 16px;
font-weight: 400;
margin: auto 0;
}
.quip-card .mdl-list__item-avatar {
vertical-align: middle;
margin: auto 10px auto 0;
}
.quip-card .mdl-card__supporting-text {
color: rgba(0, 0, 0, .87);
}
.quip-card .mdl-card__title-text {
display: block;
}
.quip-card .mdl-card__title-text .quip-author {
display: inline-block;
color: #777;
font-size: 12px;
margin: auto 0 auto 5px;
}
.quip-card .mdl-card__title-text .mdl-card__subtitle-text {
display: block;
font-size: 12px;
}
.quip-card > .mdl-card__actions .mdl-button--icon {
overflow: visible;
}
.quip-card > .mdl-card__actions {
display: flex;
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(micons.eot);
src: url(regular.woff2) format('woff2');
}
.material-icons {
......
'use strict';
const {ipcRenderer} = require('electron');
const $ = require('jquery');
const api = require('../src/common/endpoints');
const url = require('url');
const OAuth = require('oauth');
let apiEndpoint = null;
let oauth = null;
let access_token = null;
let promises = {
oauth: null,
token: null
};
ipcRenderer.send('req-oauth-obj', null);
ipcRenderer.on('rec-oauth-obj', (event, data, tokens) => {
console.log(data);
console.log(tokens);
promises.oauth = Promise.resolve(data);
promises.token = Promise.resolve(tokens);
resolvePromises();
});
// Resolve our promises
let resolvePromises = () => {
promises.oauth.then(data => {
apiEndpoint = data._requestUrl.substring(0, data._requestUrl.indexOf('api'));
oauth = new OAuth.OAuth(
url.resolve(apiEndpoint, api.OAUTH_REQUEST_TOKEN),
url.resolve(apiEndpoint, api.OAUTH_ACCESS_TOKEN),
data.consumer_key,
data.consumer_secret,
'1.0',
'oob',
'HMAC-SHA1'
);
}).catch(err => {
console.error(err.stack);
});
promises.token.then(data => {
access_token = data;
loadPublicTimeline();
}).catch(err => {
console.error(err.stack);
});
}
/*
<div class="quip-card mdl-card mdl-shadow--2dp">
<div class="mdl-card__title mdl-card--border">
<img class="mdl-list__item-avatar" src="https://quitter.se/avatar/113454-96-20130601134715.jpeg"></img>
<div class="mdl-card__title-text">
<span>McScx</span>
<span class="quip-author">@mcscx</span>
<span class="mdl-card__subtitle-text">2h ago</span>
</div>
</div>
<div class="mdl-card__supporting-text">
@moshpirit cookies, JS and HTPPS work fine. A disadvantage of #Lightning is (to my knowledge): there are no addons. But I can live with that
</div>
<div class="mdl-card__actions mdl-card--border">
<a class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
<i class="material-icons">reply</i>
</a>
<a class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
<i class="material-icons">repeat</i>
</a>
<a class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
<i class="material-icons">star_rate</i>
</a>
</div>
</div>
*/
let loadPublicTimeline = () => {
oauth.get(url.resolve(apiEndpoint, api.PUBLIC_TIMELINE), access_token.public, access_token.secret, (err, json, response) => {
let data = JSON.parse(json);
for (var i = 0; i < data.length; i++) {
let cardDiv = document.createElement('div');
cardDiv.className = 'quip-card mdl-card mdl-shadow--2dp';
let innerDiv1 = document.createElement('div');
innerDiv1.className = 'mdl-card__title mdl-card--border';
let userAvatar = document.createElement('img');
userAvatar.className = 'mdl-list__item-avatar';
userAvatar.src = data[i].user.profile_image_url_profile_size;
let userInfoDiv = document.createElement('div');
userInfoDiv.className = 'mdl-card__title-text';
let userName = document.createElement('span');
userName.textContent = data[i].user.name;
let userNick = document.createElement('span');
userNick.className = 'quip-author';
userNick.textContent = `@${data[i].user.screen_name}`;
let quipTime = document.createElement('span');
quipTime.className = 'mdl-card__subtitle-text';
quipTime.textContent = `${timeSinceQuip(data[i].created_at)} ago`;
quipTime.title = dateString(data[i].created_at);
userInfoDiv.appendChild(userName);
userInfoDiv.appendChild(userNick);
userInfoDiv.appendChild(quipTime);
innerDiv1.appendChild(userAvatar);
innerDiv1.appendChild(userInfoDiv);
let innerDiv2 = document.createElement('div');
innerDiv2.className = 'mdl-card__supporting-text';
innerDiv2.innerHTML = data[i].statusnet_html;
let innerDiv3 = document.createElement('div');
innerDiv3.className = 'mdl-card__actions mdl-card--border';
let actionButton = document.createElement('label');
actionButton.type = 'button'
actionButton.className = 'mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect';
let icon = document.createElement('i');
icon.className = 'material-icons mdl-badge';
icon.textContent = 'reply';
actionButton.appendChild(icon);
innerDiv3.appendChild(actionButton);
actionButton = document.createElement('label');
actionButton.type = 'button'
actionButton.className = 'mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect';
icon = document.createElement('i');
icon.className = 'material-icons mdl-badge';
icon.textContent = 'repeat';
icon.setAttribute('data-badge', data[i].repeat_num);
actionButton.appendChild(icon);
innerDiv3.appendChild(actionButton);
actionButton = document.createElement('label');
actionButton.type = 'button'
actionButton.className = 'mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect';
icon = document.createElement('i');
icon.className = 'material-icons mdl-badge';
icon.textContent = 'star_rate';
icon.setAttribute('data-badge', data[i].fave_num);
actionButton.appendChild(icon);
innerDiv3.appendChild(actionButton);
cardDiv.appendChild(innerDiv1);
cardDiv.appendChild(innerDiv2);