Using CloudShout to build a SocialBrowse-like Service (Part 3)

Welcome back for part 3 of a series of posts on how to build a SocialBrowse like service with CloudShout. You can find previous posts in this series here (Part 1) and here (Part 2).

When we last left off we had accomplished the following tasks:

  • Set up our test environment
  • Created a new app at CloudShout
  • Set up the basis of our JS code
  • Coded the methods to attach small icons by all text links on page
  • Coded the methods to open the link sharing dialog

And we last left off with 2 hours and 15 minutes invested into the project.

My goals for this post have changed a bit since the end of part 2. The goals I set out when i began coding for this session where:

  • Create sidebar widget
  • Have links share to all my friends
  • Build a commenting system
  • Icons by the links in pages should changed based on who shared them


Create the Sidebar Widget

In the previous post, we attached icons to all links on the page. Clicking these icons would open a dialog window that allowed the user to title the link and give it a description. Submitting this form would simply alert the data, so now we need to actually send it to our friends, and have a way for it to display to them.

We want our sidebar widget to draw as soon as possible. This is very different from the share pop-up dialog, which appears when you click on the share icon. Thus, we want to create the widget when our application is initialized. Our init function is where such instructions should go, and the call to create a sidebar widget is quiet simple:


this.init = function(){
this.create_sidebar()
this.convert_all_links()
}

That we added here is a call to create the visuals of the sidebar widget, defined in “create_sidebar”:


this.create_sidebar = function(){
itkp.new_widget(this.info.id, "LinkShout", 200, 300)
itku.create(this.elms.links)
}

The first thing we do here is call the ITK palette object to create a new widget, passing it an id, text for the title bar, and a width/height. Secondly, we need to create the div that all records will draw in. itku.create is a function we ran into in Part 2. However, in part 2 we passed our JSON in as the parameter. We are doing that here too, but for cleanliness, we stored the JSON data in the this.elms object and reference it here through this.elms.links


this.elms = {
links: {
elm: "div",
parent_elm: this.info.id + "_content",
content: { type: "html", value: "" },
atts: { id: this.info.id + "_links", className: "itkp_rc" },
styles: { position: "absolute", overflow: "hidden", top: "5px", left: "5px", width: "156px", height: "205px", backgroundColor: "#fff", fontSize: "10px", fontFamily: "verdana", color: "#666" }
}
}

this.elms is my JSON DOM elements repository that I’ll keep my element information stored in. Notice the atts property of “links”. It has an id attribute, which is set to this.info.id + “_links”, and will serve as our way of calling this div in the future to insert shared links into it. You should append all your ID’s with the id of your app so that it will not conflict with other apps or existing page elements.

Let’s see if our sidebar widget worked:



Sharing

Now, our old share_link function simple grabbed our input from the share form and alerted it. Let’s see what’s changed:


this.share_link = function(){
var say = escape(itku.id(this.info.id + "_" + this.link_id + "_say_input").value)
var title = escape(itku.id(this.info.id + "_" + this.link_id + "_title_input").value)
var id = this.permalink(this.link_url)
var link = itk3.user.id + "***" + this.link_url + "***" + title + "***" + say + "***" + id
var msg = this.info.id + ".incoming_link^^^" + link
this.close(this.info.id + "_" + this.link_id)
this.incoming_link(link)
for(var i in itk3.user.friends){
itkc.message("app", "visitor_"+i+"_apps", "visitor_"+i+"_apps", "user_message", msg)
}
}

After we set some variables, which all should be self explanatory (remember: itku.id() is just a short cut for document.getElementById()). The first line we come to after all that is this.close(). This calls our close function where we can do any cleanup we need to do, then close the share link pop-up dialog. Then we have incoming_link(). This is what will populate the newly shared link in OUR sidebar. We’ll look at that function later. But note, I’m only putting it in there for myself, the following for loop is where we send it to our friends. Since I’m not my own friend (sad, i know), I’ll never get the message of a new link. Thus, that is why we call that function directly.

Now, the for loop will loop through each of our friends, firing a message to them. Look back up where the msg variable is being set. If you look before the “^^^”, you’ll notice that it’s building a callback to this.incoming_link, where “this” is actually the app name because the framework will recieve the incoming message and will need to know where to direct it.

itkc.message is a way to communicate with CloudShout. We can see from the parameters that we are sending an “application” message, to be handled with the “visitor_YOURID_apps” application, child instance “visitor_YOURID_apps” (these are the same because the app we are sending to only has one instance, some apps have more than one). Within that app and instance, we are pointing to the “user_message” function, and it’s parameter will be “msg”.

Don’t worry, this is all documented here. 🙂

So, when this fires off, all of my friends that are currently connected, will get a message to their “visitor” app, specifically to the user_message function. This function will parse in the data and create a call out to their LinkShout app. In turn, on their end, they will see the link appear in the sidebar area as I did. Pretty cool, huh?

Let’s see it in action:



The user “dang” shared a link. Upon clicking the share button, the dialog closed, and the links where sent out to all friends. The received links then show up in the sidebar widget.

Also, the icons by the links changed. It changed on three links because they all link to the same place. On “dang’s” side, the icon turned into an asterix, to denote that “I” shared the link. On “dascgo’s” side, the icon changed to a red circle with an exclamation point, indicating that a friend of mine shared a link that I can currently see. And yeah, I know, they make no sense visually, but I’m not in design mode right now… the png’s can be swapped out later.

Let’s talk about how we got the link to appear in the sidebar and how we changed the icons.


Handling a Shared Link

We know that the function that handles this is called “incoming_link”. Here’s what id does:


this.incoming_link = function(msg){
var msg = msg.split("***")
var friend = (msg[0] != itk3.user.id) ? msg[0] : itk3.user.id
var url = unescape(msg[1])
var title = unescape(msg[2])
var comment = unescape(msg[3])
var id = msg[4]
var comment_count = 0
var links_count = 1
var shared_at = dateFormat(Date(), "m/d/yy@h:MMtt")
if(itku.id(this.info.id + "_link_count_for_" + id)){
this.increment_links(id)
}else{
this.create_link_record(friend, id, url, title, comment, comment_count, links_count, shared_at)
}
var who = (friend == itk3.user.id) ? "me" : "you"
this.change_link_to(who, url)
}

The first this is we take the string parameter and split it up. Right now, CloudShout messages are all strings. You will need to set a delimiter in your strings that you can parse on. In the future, we will move further towards using JSON to pass around messages.

This string is split and we assign our variables, then we hit an if statement. What we are looking for is if that link is already shared in the sidebar. If so, we don’t want to list it again. Instead, there is a number by the link ( “[1]” in the images above) that should increment. Otherwise, we need to create the record in that div with create_link_record().

Finally, after we do that, we change the icons by the links with “change_link_to()” and pass it “me” or “you” depending on if I shared it or one of my friends did.

I’ll show you the “create_link_record”, but I don’t think it warrants discussion. It simply builds a string of HTML that we will use innerHTML to set. Following, we will look at the “change_link_to()” function:


this.create_link_record = function(friend, id, url, title, comment, comment_count, links_count, shared_at){
var div = itku.id(this.info.id + "_links")
var a = ""
a += ""
a += " "
a += " "
a += " "
a += " "
a += "
"
a += " [" + links_count + "] "
a += " " + title + "
"
a += " " + shared_at + "
"
a += " " + comment + "
"
a += " Comments "
a += " (" + comment_count + ") "
a += "
"
a += "
"
div.innerHTML = a + div.innerHTML
}

Changing the Icons by Links


this.change_link_to = function(which, url){
var links = document.getElementsByTagName("a")
var link_count = -1
for(var i = 0; i < links.length; i++){
var link = links[i]
var name = link.getAttribute("name")
var href = link.getAttribute("href")
if(!name || name != (this.info.id + "_link")){
link_count++
if( url == href ) itku.id(this.info.id + "_link_" + link_count).src = this.img(which)
}
}
}

The above function will change the share icons by all matching links. Simply put, we cycle through all the links on the page. If we find a link that matches the url shared, we change the icon. I had a serious WTF issue here… it would always change 1 more link than it should when shared. Meaning if there was one link on the page, it’s icon would change, and so would the first link immediately following the sidebar widget in the DOM. I was just about to call it quits for the night when I realized it was now seeing the url IN the sidebar widget and trying to change the icon to it, which due to the array, was the icon by the link immediately following the sidebar. Ugh. That one took awhile and a LOT of console.logs to spot. Discovering that, I decided to add a name attribute to all anchor tags the sidebar creates so we can ignore those.

Also, in this function is a call to an image helper function img(). LinkShout currently only needs three images: The share icon, the shared by you icon, and the shared by me icon. So this is a helper function to swap between the three states.

Comments

All link records will have a comment link with a numeric indicator of the number of current comments. This link should open the Comments dialog for that link. Basically… a chat focused on the shared link.

When you click that link, this function is fired:


this.show_comments = function(icon, id, url, title, comment, shared_at){
itkp.new_float(this.info.id, this.info.id + "_" + id + "_comments", "LinkShout Comments", 350, 400, itkp.center_vertically(400), itkp.center_horizontally(350))
itku.create(this.elms.comments_header)
itku.create(this.elms.comments_holder)
itku.create(this.elms.comments_input)
itku.create(this.elms.comments_button)
itku.id(this.info.id + "_" + id + "_comments_header").innerHTML = this.create_comment_header(icon, url, title, comment, shared_at)
}

We create a new floating dialog window, create it’s various divs for layout, and set the content of the header div. “create_comment_header()” is a HTML builder function:


this.create_comment_header = function(icon, url, title, comment, shared_at){
var a = ""
a += ""
a += " "
a += " "
a += " "
a += " "
a += "
"
a += " " + shared_at + "
"
a += " " + title + "
"
a += "
"
return a
}

So now we have a comment interface:
But, how does it work? Very, very similar to the way sharing a link works, actually! If I click on the “say” button, the following function is called:


this.send_comment = function(id){
var input = itku.id(this.info.id + "_" + id + "_comment_input")
var comment = itk3.user.id + "***" + id + "***" + escape(input.value) + "***" + dateFormat(new Date(), "m/d/yy@h:MMtt")
var msg = this.info.id + ".incoming_comment^^^" + comment
input.value = ""
this.incoming_comment(comment)
for(var i in itk3.user.friends){
itkc.message("app", "visitor_"+String(i)+"_apps", "visitor_"+String(i)+"_apps", "user_message", msg)
}
}

We set our values, clear out the input area, display it to ourselves, and then loop through our friends and send it out to them.


Handling Incoming Comments

The handler function for this call is:


this.incoming_comment = function(msg){
msg = msg.split("***")
var friend = (msg[0] != itk3.user.id) ? msg[0] : itk3.user.id
var id = msg[1]
var comment = msg[2]
var when = msg[3]
$("#" + this.info.id + "_" + id + "_comments_for").append(this.format_comment(friend, comment, when))
this.increment_comments(id)
this.scroll_comment_area()
}

We split our message string and set some variables. Then we append the HTML content generated in the next function, “format_comment()”, into our main comments div. Finally, we want to call two helper functions to increment the number of comments in the sidebar widget, and, if the comments dialog for that link is open, scroll the div to the newest comment. The HTML builder function is as follows:


this.format_comment = function(friend, comment, when){
var a = ""
a += ""
a += " "
a += " "
a += " "
a += " "
a += "
" + unescape(when) + "
" + unescape(comment) + "
"
return a
}

Let’s see if that worked:


Helpers

I’ve created a bunch of little helper functions for this app. Let’s briefly look over them here:


this.permalink = function(url){
return url.replace(/[^a-zA-Z 0-9]+/g,'_')
}

this.img = function(which){
return this.gfx + which + "_share_icon.png"
}

this.user_icon = function(friend){
icon = (friend != itk3.user.id) ? itk3.user.friends[friend].icon : itk3.user.icon
return itku.icon(icon)
}

this.increment_links = function(id){
var count = parseInt(itku.id(this.info.id + "_link_count_for_" + id).innerHTML)
count++
itku.id(this.info.id + "_link_count_for_" + id).innerHTML = count
}

this.increment_comments = function(id){
var count = parseInt(itku.id(this.info.id + "_comment_count_for_" + id).innerHTML)
count++
itku.id(this.info.id + "_comment_count_for_" + id).innerHTML = count
}

this.scroll_comment_area = function(){
div = itku.id(this.info.id + "_" + id + "_comments_for")
div.scrollTop = div.scrollHeight
}

I also created a this.gfx variable for my app which points to the path for my images:


this.gfx = "http://localhost/linkshout/" //itk3.paths.apps + "gfx/apps/izea/linkshout/"

It’s local for now, but will swap it out when I publish my app.


Wrap Up for Part 3

It’s hard to see how cool this is from just screenshots. At the end of this project, I’ll screencast a demo for everyone.

So, let’s see where we are so far. We now have the ability to:

  • Share a link with friends
  • Display these links to our friends
  • Chat about the links in real time
  • Have visual que’s as to who’s shared a link (the icons by the links)

Not bad, and development time for the above is 4 hours. I’d say a good solid hour of that was me pulling out my hair with that “change_link_to()” function, but that’s how it goes sometimes.

Here’s our hours so far:

  • 00.75 hrs – Project planning
  • 00.25 hrs – Test environment setup
  • 01:25 hrs – Day one programming
  • 04:00 hrs – Day two programming
  • ——————————-
  • 06:25 hrs – Total time

So what do I hope to accomplish next? I’m not sure, it depends on what solutions come to me while I’m not working on this. But here are some of the remaining issues that need to be addressed:

  • on the initial share description, add a counter to the number of characters left that the user can input, and limit them to 140 characters
  • same on the comments
  • share link branding
  • when two or more people share the same link, but with different titles, we need to handle that in the sidebar. have a count that shows how many people shared the same link and that will link to a screen that shows all the titles and descriptions for that link.
  • save data to a database
  • initial pull in of data form database
  • scrolling on the link sharing sidebar widget

Not too much more work. Saving the data is probably the biggest issue in there.

I hope to see you next post!

Advertisements