Skip to content

Commit

Permalink
Merge: Twitter Timeline の表示機能
Browse files Browse the repository at this point in the history
  • Loading branch information
nobuoka committed Dec 22, 2012
2 parents 9814248 + df935ac commit f870b6d
Show file tree
Hide file tree
Showing 10 changed files with 887 additions and 7 deletions.
7 changes: 7 additions & 0 deletions MeteorLine/MeteorLine.jsproj
Expand Up @@ -68,7 +68,14 @@
<Content Include="js\vividcode\meteorline\ui\AccountListView.css" />
<Content Include="js\vividcode\meteorline\ui\AccountListView.html" />
<Content Include="js\vividcode\meteorline\ui\AccountListView.js" />
<Content Include="js\vividcode\meteorline\ui\TimelinesContainerView.css" />
<Content Include="js\vividcode\meteorline\ui\TimelinesContainerView.html" />
<Content Include="js\vividcode\meteorline\ui\TimelinesContainerView.js" />
<Content Include="js\vividcode\meteorline\ui\TimelineView.css" />
<Content Include="js\vividcode\meteorline\ui\TimelineView.html" />
<Content Include="js\vividcode\meteorline\ui\TimelineView.js" />
<Content Include="js\vividcode\twitter\OAuthCredentialsObtainer.js" />
<Content Include="js\vividcode\twitter\TwitterClient.js" />
<Content Include="pages\home\home.css" />
<Content Include="pages\home\home.html" />
<Content Include="pages\home\home.js" />
Expand Down
80 changes: 80 additions & 0 deletions MeteorLine/js/vividcode/meteorline/ui/TimelineView.css
@@ -0,0 +1,80 @@

.timeline-view-component {
display: -ms-grid;
-ms-grid-columns: 1fr;
-ms-grid-rows: 1fr;
}
.timeline-view-component.hide {
display: none;
}

.timeline-view-component > section{
border: solid 2px #666666;
border-radius: 8px;
padding-top: 8px;

display: -ms-flexbox;
-ms-flex-direction: column;
}
.timeline-view-component > section h1 {
font-size: 120%;
margin: 5px 8px;
}
.timeline-view-component .status-list {
-ms-flex: 1 1 auto;
overflow: auto;
}


.timeline-view-component .status {
position: relative;
margin: 5px 8px;
padding: 3px;
padding-left: 55px;
background-color: rgba(0, 0, 0, 0.7);
min-height: 48px;

overflow: auto;
}
.timeline-view-component .status .profile-image {
position: absolute;
top: 3px; left: 3px;
width: 48px; height: 48px;
}
.timeline-view-component .status .screen-name {
opacity: 0.6;
}

/* ---- stream の状態 ---- */
.timeline-view-component .stream-control > .stream-working,
.timeline-view-component .stream-control > .stream-not-working {
text-align: right;
margin-right: 8px;
}
.timeline-view-component .stream-control > .stream-working.hide,
.timeline-view-component .stream-control > .stream-not-working.hide {
display: none;
}

/* ---- 投稿フォーム ---- */
.timeline-view-component .tweet-form {
margin: 8px;
}
.timeline-view-component .tweet-form:not(.active) textarea {
height: 1.8em;
min-height: 1.8em;
}
.timeline-view-component .tweet-form textarea {
height: 4em;
width: 100%;
box-sizing: border-box;
}
.timeline-view-component .tweet-form:not(.active) button {
display: none;
}
.timeline-view-component .tweet-form.progress button {
display: none;
}
.timeline-view-component .tweet-form:not(.progress) progress {
display: none;
}
52 changes: 52 additions & 0 deletions MeteorLine/js/vividcode/meteorline/ui/TimelineView.html
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta charset="utf-8" />
<title>TimelineView</title>

<!-- WinJS 参照 -->
<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

<script src="/js/vividcode/meteorline/Config.js"></script>
<script src="/js/vividcode/twitter/TwitterClient.js"></script>

<link href="TimelineView.css" rel="stylesheet" />
<script src="TimelineView.js"></script>
</head>
<body class="timeline-view-component">
<div class="twitter-status-view-template" data-win-control="WinJS.Binding.Template">
<div class="status">
<div class="user-box">
<img class="profile-image" src="#" data-win-bind="src: user.profile_image_url_https" />
<span class="name" data-win-bind="textContent: user.name"></span>
<span class="screen-name">@<span data-win-bind="textContent: user.screen_name"></span></span>
</div>
<div class="text" data-win-bind="textContent: text"></div>
</div>
</div>
<section>
<h1>@<span data-win-bind="textContent: screenName"></span> - Home Timeline</h1>
<div class="stream-control">
<div class="stream-working hide">
<span class="stream-status">user stream is working</span>
<button class="stream-status-toggle-request">停止</button>
</div>
<div class="stream-not-working">
<span class="stream-status">not working</span>
<button class="stream-status-toggle-request">開始</button>
</div>
</div>
<div class="tweet-form">
<textarea></textarea>
<progress></progress>
<div class="error-message"></div>
<button class="tweet-button">投稿</button>
<button class="cancel-button">閉じる</button>
</div>
<div class="status-list"></div>
</section>
</body>
</html>
197 changes: 197 additions & 0 deletions MeteorLine/js/vividcode/meteorline/ui/TimelineView.js
@@ -0,0 +1,197 @@
// ページ コントロール テンプレートの概要については、次のドキュメントを参照してください:
// http://go.microsoft.com/fwlink/?LinkId=232511
(function () {
"use strict";

var CONSUMER_KEY = vividcode.meteorline.Config.twitter.consumerKey;
var CONSUMER_SECRET = vividcode.meteorline.Config.twitter.consumerSecret;

var TimelineView = WinJS.UI.Pages.define("/js/vividcode/meteorline/ui/TimelineView.html", {
/// <field>アカウント情報</field>
account: null,

/// <field type="vividcode.twitter.TwitterClient">twitter client</field>
_client: null,
/// <field type="String">最後に追加された status の Id</field>
_idStrOfLastAddedStatus: '',
/// <field type="Boolean">中断前にストリームが動いていたかどうか</field>
_isStreamWorkingBeforeSuspend: false,
/// <field type="Number">一覧の中に表示する要素の最大数</field>
_maxNumItems: 100,

init: function (element, options) {
element.classList.add("timeline-view-component");
this.account = options.account;
if (options.initHide) this.hide();

this._client = new vividcode.twitter.TwitterClient({ key: CONSUMER_KEY, secret: CONSUMER_SECRET }, this.account.tokenCreds);
},

processed: function (element, options) {
var h1Elem = element.querySelector("section h1");
WinJS.Binding.processAll(h1Elem, { screenName: this.account.screenId });
},

ready: function (element, options) {
var that = this;

// 投稿周り
var tweetFormElem = element.querySelector("section .tweet-form");
tweetFormElem.querySelector("textarea").addEventListener("activate", function (evt) {
evt.currentTarget.parentNode.classList.add("active");
tweetFormElem.querySelector(".error-message").textContent = "";
}, false);
tweetFormElem.querySelector("button.cancel-button").addEventListener("click", function (evt) {
evt.currentTarget.parentNode.classList.remove("active");
tweetFormElem.querySelector(".error-message").textContent = "";
}, false);
tweetFormElem.querySelector("button.tweet-button").addEventListener("click", function (evt) {
var status = tweetFormElem.querySelector("textarea").value;
tweetFormElem.querySelector(".error-message").textContent = "";
tweetFormElem.classList.add("progress");
that._client.postStatus(status).then(function () {
tweetFormElem.querySelector("textarea").value = "";
tweetFormElem.classList.remove("active");
}, function onError(err) {
tweetFormElem.querySelector(".error-message").textContent = "投稿に失敗しました : " + err.description;
console.dir(err);
}).then(function () {
tweetFormElem.classList.remove("progress");
});
}, false);


this._client.onuserstreammessage = function (json) {
if (json.text) {
that.__pushStatus(json);
} else {
console.dir(json);
}
};
this._client.onuserstreamstart = function () {
element.querySelector(".stream-control > .stream-working").classList.remove("hide");
element.querySelector(".stream-control > .stream-not-working").classList.add("hide");
};
this._client.onuserstreamstop = function () {
element.querySelector(".stream-control > .stream-working").classList.add("hide");
element.querySelector(".stream-control > .stream-not-working").classList.remove("hide");
};

element.querySelector(".stream-control > .stream-working button").addEventListener("click", function (evt) {
that._client.stopUserStream();
}, false);
element.querySelector(".stream-control > .stream-not-working button").addEventListener("click", function (evt) {
that.__updateByGettingHomeTimeline();
that._client.startUserStream();
}, false);

// アプリの再開や終了の際のイベントリスナ
this._suspendingListener = function (evt) {
that._isStreamWorkingBeforeSuspend = that._client.isUserStreamWorking();
that._client.stopUserStream();
};
this._resumingListener = function (evt) {
// とりあえず今のところはアプリ再開時に自動でユーザーストリームを開始しない
// アプリ切り替えを多くする場合などに困るので
if (that._isStreamWorkingBeforeSuspend) {
//that.__updateByGettingHomeTimeline();
//that._client.startUserStream();
}
};
Windows.UI.WebUI.WebUIApplication.addEventListener("suspending", this._suspendingListener, false);
Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", this._resumingListener, false);
},

// 終了処理
unload: function () {
Windows.UI.WebUI.WebUIApplication.removeEventListener("suspending", this._suspendingListener, false);
Windows.UI.WebUI.WebUIApplication.removeEventListener("resuming", this._resumingListener, false);
this._client.stopUserStream();
},

show: function () {
this.element.classList.remove("hide");
},
hide: function () {
this.element.classList.add("hide");
},

__updateByGettingHomeTimeline: function () {
var that = this;
this._client.getHomeTimeline().done(function (homeTimelineStatuses) {
//console.dir(homeTimelineStatuses);
var statuses = [];
homeTimelineStatuses.reverse().forEach(function (status) {
var oid = that._idStrOfLastAddedStatus;
var nid = status.id_str;
if (oid.length > nid.length || (oid.length === nid.length && oid >= nid)) {
// ID が古い場合は何もしない
return;
}
statuses.unshift(status);
});
that.__pushStatuses(statuses);
}, function onError(err) {
console.error(err);
});
},
__pushStatus: function (status) {
this.__pushStatuses([status]);
},
__pushStatuses: function (statuses) { // statuses のインデックスの小さい方が新しいツイート
if (statuses.length === 0) return;

var that = this;

// statuses の個数が多すぎる場合は削除
while (statuses.length > that._maxNumItems) {
statuses.pop();
}

var statusTemplateElem = this.element.getElementsByClassName("twitter-status-view-template").item(0);
this._idStrOfLastAddedStatus = statuses[0].id_str;
//console.log(status.user.screen_name + ": " + status.text);
/// <var name="statusTemplate" type="WinJS.Binding.Template">ツイッターの status を表示する HTML 要素のテンプレート</var>
var statusTemplate = statusTemplateElem.winControl;//this.element.querySelector(".status-template").winControl;
var statusListElem = this.element.querySelector(".status-list");
var statusElemPromises = statuses.map(function (status) {
return statusTemplate.render(status);
});
WinJS.Promise.join(statusElemPromises).done(function (statusElems) {
var affectedItems = statusListElem.querySelectorAll(".win-template");
var affectedItemArray = [];
for (var i = 0; i < affectedItems.length; ++i) {
affectedItemArray.push(affectedItems.item(i));
}
var mni = that._maxNumItems - statuses.length;
while (affectedItemArray.length > mni) {
statusListElem.removeChild(affectedItemArray.pop());
}

// Create addToList animation.
var addToList = WinJS.UI.Animation.createAddToListAnimation(statusElems, affectedItemArray);

var f = document.createDocumentFragment();
statusElems.forEach(function (statusElem) {
f.appendChild(statusElem);
});
// Insert new item into DOM tree.
// This causes the affected items to change position.
statusListElem.insertBefore(f, statusListElem.firstChild);

// Execute the animation.
addToList.execute();
});
},

updateLayout: function (element, viewState, lastViewState) {
/// <param name="element" domElement="true" />

// TODO: viewState の変更に対応します。
}
});

WinJS.Namespace.define("vividcode.meteorline.ui", {
TimelineView: TimelineView
});
})();
32 changes: 32 additions & 0 deletions MeteorLine/js/vividcode/meteorline/ui/TimelinesContainerView.css
@@ -0,0 +1,32 @@

.timelines-container-view-component {
display: -ms-grid;
-ms-grid-columns: 100%;
-ms-grid-rows: 100%;
}
.timelines-container-view-component.hide {
display: none;
}

.timelines-container-view-component .vc-viewport {
overflow-y: hidden;
overflow-x: auto;
}
.timelines-container-view-component .vc-surface {
display: inline-block;
height: 100%;
box-sizing: border-box;
}
.timelines-container-view-component .timeline-views-container {
display: -ms-flexbox;
-ms-flex-direction: row;
padding: 15px 8px 35px 8px;
overflow: hidden;
height: 100%;
box-sizing: border-box;
}
.timelines-container-view-component .timeline-views-container > .timeline-view-component {
width: 300px;
height: 100%;
margin: 0 5px;
}

0 comments on commit f870b6d

Please sign in to comment.