Skip to content

Commit

Permalink
Merge pull request #560 from csync/develop
Browse files Browse the repository at this point in the history
1.2.0
  • Loading branch information
dfirsht authored Apr 3, 2017
2 parents d597c2f + 63b6f42 commit cb4b0e3
Show file tree
Hide file tree
Showing 103 changed files with 6,149 additions and 204 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ yarn.lock
backend/public/app/bower_components
backend/public/app/credentials.js

# StreamBot
stream-bot/config/private
stream-bot/node_modules
stream-bot/.idea

# Frontend
iOS/TogetherStream/TogetherStream/Configuration/private.plist

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ You can skip installing Google Analytics by removing the method `setupGoogleAnal
2. Replace the `sessionSecret` with a unique string. This is used to encrypt the session tokens.
3. Replace the email `userName` and `domainName` with your email address (i.e. `userName@domainName`), the `password` with your email password, and the `displayUserName` and `displayDomainName` with what you want the emails to be sent from. You can use the same values for here as your actual `userName` and `domainName` if you wish.


### Frontend Configuration
1. In `backend/public/app/credentials.js` replace `YOUR_PRODUCTION_FACEBOOK_APP_ID` and `YOUR_DEVELOPMENT_FACEBOOK_APP_ID` with the ids for your production and development Facebook app ids respectively.
2. Replace `YOUR_YOUTUBE_API_KEY` with Youtube's API key provided by Google.
3. Replace the email `YOUR_CSYNC_SEVER_IP_ADDRESS` and `YOUR_CSYNC_SEVER_PORT` with your csync server's ip address and port.

### Cloud Configuration
1. Go to https://console.ng.bluemix.net and create an account if you do not already have one.
2. Click "Create a Service", choose "Compose for PostgreSQL" and create it.
Expand Down Expand Up @@ -94,3 +100,6 @@ Together Stream is intended solely for use with an Apple iOS product and intende
## Licenses
[iOS](LICENSE-IOS)
[Non-iOS](LICENSE-NON-IOS)

## Contribution Guide
Want to contribute? Take a look at our [CONTRIBUTING.md](.github/CONTRIBUTING.md)
18 changes: 3 additions & 15 deletions backend/public/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,16 @@
ga('send', 'pageview');

</script>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:500,400,300,300italic,700italic,700,400italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Work+Sans:500,400,300,300italic,700italic,700,400italic' rel='stylesheet' type='text/css'>
<link rel="manifest" href="./manifest.json">
<script src="./vendor/csync_v1.1.0.js"></script>
<script src="./bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="./src/ts-app.html">
<link rel="stylesheet" href="/stylesheets/common.css">
<link rel="stylesheet" href="/stylesheets/main.css">
<link rel="stylesheet" href="/stylesheets/main.css">
</head>
<body>
<ts-app></ts-app>
<footer>
<nav class="nav-footer">
<ul class="links">
<li>© Copyright IBM Corporation 2017. All Rights Reserved.</li>
<li>
<ul class="more-links">
<li class="link"><a onclick="ga('send', 'event', 'link', 'click', 'license');" href="http://ibm.biz/license-non-ios">License</a></li>
<li class="link"><a onclick="ga('send', 'event', 'link', 'click', 'privacy-policy');" href="http://ibm.biz/together-stream-privacy-policy">Privacy</a></li>
<li class="link"><a onclick="ga('send', 'event', 'link', 'click', 'feedback-2');" href="http://ibm.biz/together-stream-feedback">Feedback</a></li>
<li style="display:none" class="link"><a onclick="ga('send', 'event', 'link', 'click', 'developers');" href="http://ibm.biz/together-stream-developers-page">Developers</a></li>
</ul>
</ul>
</nav>
</footer>
</body>
</html>
113 changes: 100 additions & 13 deletions backend/public/app/src/behaviors/csync-behavior.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,38 @@
viewerHeartbeatTimeoutSecs: {
type: Number,
value: 4000
},
/**
* Used to keep track of whether authentication with csync has happened.
*/
csyncAuthSuccess: {
type: Boolean,
value: false
},
/**
* Used to keep track of when stream is active and when it ends.
*/
isStreamActive: {
type: Boolean,
value: false
},
/**
* Stores active listeners here for cleanup.
*/
activeListeners: {
type: Array,
value: function () {
return []
}
},
/**
* Stores active intervals here for cleanup.
*/
activeIntervals: {
type: Array,
value: function () {
return []
}
}
},
/**
Expand Down Expand Up @@ -63,7 +95,11 @@
let errorCallback = function (err) {
console.error(err)
}
this.csyncApp.authenticate("facebook", facebookAccessToken).then(successCallback, errorCallback)
let localSuccessCallback = function () {
this.set("csyncAuthSuccess", true)
successCallback()
}.bind(this)
this.csyncApp.authenticate("facebook", facebookAccessToken).then(localSuccessCallback, errorCallback)
},
/**
* Setting up the csync listeners that change as properties are updated from ios app.
Expand All @@ -73,6 +109,23 @@
this.setupChatCsyncListener()
this.setupHeartbeatCsyncListener()
this.setupParticipantsCsyncListener()
this.setupHeartbeatCsyncWriter()
},
/**
* Called when stream ends to cleanup and allow for another stream to be listened to.
*/
cleanupListeners: function () {
this.set("streamValidated", false)
this.set("chatMessages", [])

this.activeListeners.forEach(function(listenerKey) {
listenerKey.unlisten()
})
this.activeIntervals.forEach(function(intervalId) {
clearInterval(intervalId)
})
this.set("activeListeners", [])
this.set("activeIntervals", [])
},
/**
* Listens to video key changes.
Expand All @@ -97,9 +150,16 @@
this.set("currentlyStreamingVideoData.isPlaying", value.data)
} else if (value.key.indexOf("streamName") >= 0) {
this.set("streamName", value.data)
} else if (value.key.indexOf("isActive") >= 0) {
if (this.isStreamActive && !value.data) {
alert("Stream has ended.")
this.cleanupListeners()
}
this.set("isStreamActive", value.data)
}
}.bind(this)
listenerKey.listen(listenerCallback)
this.push("activeListeners", listenerKey)
},
/**
* Listens to chat key changes.
Expand All @@ -121,6 +181,7 @@
}.bind(this)

listenerKey.listen(listenerCallback)
this.push("activeListeners", listenerKey)
},
/**
* Listens to heartbeat key changes to know how many users are currently watching.
Expand All @@ -138,31 +199,41 @@
if (!value.exists) {
return
}
this.currentViewerIds[value.creator] = Date.now()
this.set("currentViewerCount", Object.keys(this.currentViewerIds).length)
let currentTimeInSecondsFrom2001 = ((Date.now() - this.timeOffsetMilliseconds) / 1000).toFixed(6)

// Filtering out invalid heartbeats that are older than ten seconds.
if (currentTimeInSecondsFrom2001 - value.data <= 10) {
this.currentViewerIds[value.creator] = Date.now()
this.set("currentViewerCount", Object.keys(this.currentViewerIds).length)
}
}.bind(this)

listenerKey.listen(listenerCallback)
this.push("activeListeners", listenerKey)

// Setup timer to delete keys that have not received a heartbeat in 2 seconds.
setInterval(function(){
let intervalId = setInterval(function(){
Object.keys(this.currentViewerIds).forEach(function(key) {
if (Date.now() - this.currentViewerIds[key] >= this.viewerHeartbeatTimeoutSecs) {
delete this.currentViewerIds[key]
}
}.bind(this))
this.set("currentViewerCount", Object.keys(this.currentViewerIds).length)
}.bind(this), 250)
}.bind(this), 2000)
this.push("activeIntervals", intervalId)

},
/**
* Sends heartbeat for logged in user to csync server.
*/
setupHeartbeatCsyncWriter: function () {
setInterval(function(){
let intervalId = setInterval(function(){
let writeKey = this.csyncApp.key("streams." + this.streamId + ".heartbeat." + this.loggedInUserFacebookId)
writeKey.write("" + (Date.now() - this.timeOffset) / 1000, {acl: csync.acl.PublicReadWrite})
}.bind(this), 1000)
let currentTime = ((Date.now() - this.timeOffsetMilliseconds) / 1000).toFixed(6)

writeKey.write("" + currentTime, {acl: csync.acl.PublicReadWrite})
}.bind(this), 500)
this.push("activeIntervals", intervalId)
},
/**
* Listens for when a participant enters/leaves a stream.
Expand All @@ -184,17 +255,20 @@

}.bind(this)
listenerKey.listen(listenerCallback)
this.push("activeListeners", listenerKey)
},
/**
* Uses Csync to send chat message
*/
sendChatMessage: function (message) {
let writeKey = this.csyncApp.key("streams." + this.streamId + ".chat." + this.generateUUID())
let timeOffset = this.timeOffset
let timeOffsetMilliseconds = this.timeOffsetMilliseconds

window.ga('send', 'event', 'button', 'click', 'send_message');
writeKey.write(JSON.stringify({
content: message.detail,
id: this.loggedInUserFacebookId,
timestamp: "" + (Date.now() - timeOffset) / 1000 // need to save in seconds from 2001
timestamp: "" + (Date.now() - timeOffsetMilliseconds) / 1000 // need to save in seconds from 2001
}), {acl: csync.acl.PublicReadWrite})
},
/**
Expand All @@ -220,17 +294,30 @@
}

if (!this.streamValidated && value.key && value.key.indexOf(streamId) >= 0) {
this.set("streamValidated", true)
callback(true)
let isActiveListenerKey = this.csyncApp.key("streams." + streamId + ".isActive")

isActiveListenerKey.listen(function(error, value) {
// If value doesn't exist and is outdated, don't process it.
if (!value.exists) {
return
}

if (value.data && !this.streamValidated) {
this.set("streamValidated", true)
callback(true)
isActiveListenerKey.unlisten()
}
}.bind(this))
return
}

// Checking if invalid once all streams have been checked.
this.debounce("invalidStreamCallback", checkIfInvalid, 200)
this.debounce("invalidStreamCallback", checkIfInvalid, 500)
// this.processMessageAndInsertIntoArray(value)
}.bind(this)

listenerKey.listen(listenerCallback)
this.push("activeListeners", listenerKey)
}
};
</script>
3 changes: 1 addition & 2 deletions backend/public/app/src/behaviors/facebook-behavior.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,10 @@
this.set("isLoggedIn", true)
this.set("facebookAccessToken", response.authResponse.accessToken)
this.set("loggedInUserFacebookId", response.authResponse.userID)
this.setupHeartbeatCsyncWriter()
this.getFBUserInfo(this.loggedInUserFacebookId, function (response) {
this.set("loggedInUserFacebookData", response)
}.bind(this))
this.getServerAccessToken(callback)
callback && callback()
} else if (forceLogin){
window.FB.login(function(response){
this.handleGetLoginStatus(response, false, callback)
Expand Down
8 changes: 6 additions & 2 deletions backend/public/app/src/shared-styles.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
width: 100%;
height: 100%;
margin: 0 auto;
font-family: 'Work Sans', 'helvetica Neue', Helvetica, Arial, "Lucida Grande", sans-serif;
font-family: 'Work Sans';
color: rgb(52, 57, 63);
font-size: 14pt;
overflow-y: scroll;
Expand Down Expand Up @@ -86,6 +86,7 @@
}

.sticky-header {
font-family: "Work Sans";
z-index: 100;
position: fixed;
top: 0px;
Expand Down Expand Up @@ -145,7 +146,7 @@
}

.sticky-header .logo a {
margin-left: 95px;
margin-left: 20px;
}

.sticky-header .logo p {
Expand Down Expand Up @@ -231,6 +232,9 @@
color: white;
}

.sticky-header nav ul .logo a {
margin-left: 20px;
}
footer .links li {
flex: 1 1 auto;
}
Expand Down
Loading

0 comments on commit cb4b0e3

Please sign in to comment.