-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the Game Gig event page #113
Changes from 44 commits
72320db
7a65e86
b7e742c
293b6bc
d2b7d1e
c7b6695
dca8442
74cd5ee
a82c407
c63a609
4774a7a
020ca52
d675e1f
f47dec3
6f72c79
45d5ce5
502bfc6
28c410b
9a621c9
129a222
7aa6208
41b5125
2b1e486
5b809a2
452d54c
2561af5
d27d9a6
0c1f6c0
323c7d1
4776fcc
4c6d4fd
45cdae2
435e0d6
616c7d9
6e2b1c5
77631b6
b05fe5f
d24ec90
42bd916
e248f42
6bedd71
2eee8f9
30c1ff3
bc9c90c
9ad9790
403798b
a2bd02d
aca7ecb
a7931aa
e4e3bfd
d4d75a4
3daa214
8eb2d00
68bd026
757c35c
100bc45
8e41e46
c86d65e
5ec4388
d31a02e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import Foundation | ||
import Kitura | ||
import HaCTML | ||
import LoggerAPI | ||
import HeliumLogger | ||
import DotEnv | ||
import SwiftyJSON | ||
|
||
struct HackathonController { | ||
static func handler(hackathon: Hackathon) -> RouterHandler { | ||
return { request, response, next in | ||
try response.send( | ||
hackathon.node.render() | ||
).end() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
function updateCountDownTimer() { | ||
const startDate = {{startDate}}; | ||
const endDate = {{endDate}}; | ||
const CountDownTimerId = {{id}}; | ||
const CountDownTimerPreId = {{preId}}; | ||
const beforeEventMessage = {{beforeEventMessage}} | ||
const duringEventMessage = {{duringEventMessage}} | ||
const afterEventMessage = {{afterEventMessage}} | ||
|
||
const current = new Date(); | ||
|
||
let timeLeft = 0; | ||
if(current < startDate) { | ||
document.getElementById(CountDownTimerPreId).innerHTML = beforeEventMessage; | ||
timeLeft = startDate.getTime() - current.getTime(); | ||
} else { | ||
document.getElementById(CountDownTimerPreId).innerHTML = duringEventMessage; | ||
timeLeft = endDate.getTime() - current.getTime(); | ||
} | ||
|
||
// Abort if time is up | ||
if(timeLeft < 0) | ||
{ | ||
document.getElementById(CountDownTimerId).innerHTML = afterEventMessage; | ||
return; | ||
} | ||
|
||
let hours = Math.floor(timeLeft / (1000*60*60)); | ||
let mins = Math.floor(timeLeft/(1000 * 60) - hours * 60); | ||
let secs = Math.floor(timeLeft/1000 - mins * 60 - hours * 60 * 60); | ||
|
||
document.getElementById(CountDownTimerId).innerHTML = | ||
hours + ":" + (mins<10?"0":"") + mins + "<span id=\"seconds\">:" + (secs<10?"0":"") + secs +"</span>"; | ||
} | ||
|
||
updateCountDownTimer(); | ||
|
||
setInterval(updateCountDownTimer,1000); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import HaCTML | ||
import Foundation | ||
|
||
struct CountDownTimer : Nodeable { | ||
let startDate : Date | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me this reads like the date that the timer starts counting down. If this is specific to events, maybe we could use some /// doc comments on this struct |
||
let endDate : Date | ||
let id = "CountDownTimer\(UUID().description)" | ||
|
||
let preId = "CountDownTimerPre\(UUID().description)" // the id of the countdown message | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't love that we are changing the ID of an element. I expect we can get away without this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have created issue #170 for this, I think fixing this is low priority. |
||
let beforeEventMessage = "Time left to start" | ||
let duringEventMessage = "Time remaining" | ||
let afterEventMessage = "Time's up!" | ||
|
||
var node: Node { | ||
return Fragment( | ||
El.Div[Attr.className => "CountDownTimer"].containing( | ||
El.Div[ | ||
Attr.className => "CountDownTimer__pre", | ||
Attr.id => preId | ||
].containing(""), | ||
El.Div[ | ||
Attr.className => "CountDownTimer_time", | ||
Attr.id => id | ||
].containing("YOU SHOULD SEE THE TIME REMAINING HERE"), | ||
Script( | ||
file: "Hackathons/CountDownTimer.js", | ||
escapes: [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure 'escapes' is the right word to be using here. 'context' is a common alternative. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How come? I think 'context' is more confusing than 'escapes'. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current system is a half-way house anyway tbh. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about 'definitions'? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good shout. |
||
"startDate": startDate, "endDate": endDate, | ||
"id": id, "preId": preId, | ||
"beforeEventMessage": beforeEventMessage, | ||
"duringEventMessage": duringEventMessage, | ||
"afterEventMessage" : afterEventMessage | ||
] | ||
) | ||
) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
function updateClock() { | ||
const id = {{id}} | ||
const current = new Date(); | ||
document.getElementById(id).innerHTML = current.getHours()+":"+(current.getMinutes()<10?"0":"") + current.getMinutes(); | ||
} | ||
|
||
updateClock(); | ||
|
||
setInterval(updateClock,1000); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import HaCTML | ||
import Foundation | ||
|
||
struct CurrentTime : Nodeable { | ||
let id = "CurrentTime\(UUID().description)" | ||
|
||
var node: Node { | ||
return Fragment( | ||
El.Span[Attr.id => id, Attr.className => "CurrentTime"].containing("Current Time"), | ||
Script( | ||
file: "Hackathons/CurrentTime.js", | ||
escapes: ["id": id] | ||
) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
import HaCTML | ||
import Foundation | ||
|
||
// swiftlint:disable line_length | ||
|
||
|
||
extension String { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! We could move this out to somewhere else. Files for extensions conventionally take the form: e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created issue #171 for this. |
||
func idMangle() -> String { | ||
return self.replacingOccurrences(of: " ", with: "-").lowercased() | ||
} | ||
} | ||
|
||
// TODO: investigate if the information here can be extracted from the landing feature | ||
struct GameGig2017: Hackathon { | ||
let gameEngines = [ | ||
("Unreal Engine", "https://www.unrealengine.com"), | ||
("Unity", "https://unity3d.com/"), | ||
("LÖVE", "https://love2d.org/"), | ||
("GameMaker", "lhttps://www.yoyogames.com/gamemaker"), | ||
("raylib", "http://www.raylib.com/") | ||
] | ||
|
||
let socialMediaLinks = [ | ||
("Facebook Page", "https://www.facebook.com/hackersatcambridge"), | ||
("Facebook Event", "https://www.facebook.com/events/124219834921040/"), | ||
("Twitter", "https://twitter.com/hackersatcam") | ||
] | ||
|
||
let tutorials = [ | ||
("Web Dev with Mozilla", "https://globalgamejam.org/news/dev-web-mozilla") | ||
] | ||
|
||
let GoboLogo = El.A[Attr.href => "http://studiogobo.com/", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should lowercase variables/constants |
||
Attr.target => "_blank" | ||
].containing( | ||
El.Img[ | ||
Attr.src => Assets.publicPath("/images/sponsors/studiogobo-logo.svg"), | ||
Attr.alt => "Studio Gobo" | ||
] | ||
) | ||
|
||
let ElectricSquareLogo = El.A[Attr.href => "https://www.electricsquare.com/", | ||
Attr.target => "_blank" | ||
].containing( | ||
El.Img[ | ||
Attr.src => Assets.publicPath("/images/sponsors/electricsquare-logo.svg"), | ||
Attr.alt => "Electric Square" | ||
] | ||
) | ||
|
||
/** | ||
* Creates a GameGigCard element, the title becomes its id (spaces are replaced with hyphons) | ||
*/ | ||
func GameGigCard(title: String, content: Nodeable) -> Node { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not clear from reading this what a GameGigCard is used for. For the whole game gig page? For sections? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For Cards? Like, literally cards. I used the same naming conventions as for the home page here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realised that reads more aggresively than I intended. What I'm saying is, how could I word it better? Because they best way to describe these is that they literally look like floating cards hovering over the background. |
||
return El.Div[Attr.className => "GameGigCard"].containing( | ||
El.Span[ | ||
Attr.className => "GameGigCard__title", | ||
Attr.id => title.idMangle() | ||
].containing(title), | ||
content | ||
) | ||
} | ||
|
||
func ListOfLinks(dict: [(String, String)]) -> Nodeable { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yip |
||
return El.Ul.containing( | ||
dict.map { (key, value) in | ||
El.Li.containing( | ||
El.A[Attr.href => value].containing(key) | ||
) | ||
} | ||
) | ||
} | ||
|
||
func GameGigTwitterFeed() -> Nodeable { | ||
return El.Div[Attr.className => "GameGigTwitterFeed"].containing( | ||
TextNode( | ||
"<a class=\"twitter-timeline\" data-dnt=\"true\" href=\"https://twitter.com/hashtag/hacgamegig\" data-widget-id=\"927201930149093377\">#hacgamegig Tweets</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+\"://platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");</script>", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we have used multi-line strings to avoid the bunch of escapes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was added before then, and tbh the twitter feed isn't used anymore, so might as well delete this. |
||
escapeLevel: .unsafeRaw | ||
) | ||
) | ||
} | ||
|
||
func Rules() -> Nodeable { | ||
return Markdown(""" | ||
The aim of the Hackers at Cambridge Game Gig 3000 is to create a fun, | ||
exciting, original game from scratch in less than 12 hours. | ||
We've created a few simple rules to help the process go smoothly for | ||
everyone. | ||
|
||
- **Please note that Computer Science applicants for the University of | ||
Cambridge are taking CSAT tests in LT1 and LT2, so please be quiet | ||
around them. This is especially important between the hours of | ||
10:00-12:00 and 15:00-17:00 when the actual tests will be taking place. | ||
DO NOT TAKE THEIR REFRESHMENTS, WE HAVE OURS IN FW11.** | ||
|
||
- Also respect and look after the Intel Lab, FW11 and the Computer Lab. | ||
Note that food or drink may only be consumed in FW11, and we will be | ||
checking! | ||
|
||
- You can work on your game in a team of up to four people. | ||
|
||
- Game-making commences at 10:30 and finishes at 20:00. | ||
|
||
- You are free to do whatever you like with your game after the Game Gig. | ||
You own the copyright to all the material you create during the event. | ||
|
||
- You are free to use any tools or libraries available to you to create | ||
your game. You can start with any pre-existing code or content that you | ||
like and you are free to use third-party assets, as long as you let the | ||
judges know what you created yourself and what you didn't. Failure to do | ||
so could risk disqualification. | ||
|
||
- It's your responsibility to make sure that you have the right to use | ||
third-party assets (for example, that they are public domain or | ||
available under an appropriate license). | ||
|
||
- We will be taking lots of photos throughout the event, and publishing | ||
them. You and your content may appear on some of these photos. We aim to | ||
publish photos that show everyone at their best, but there might be a | ||
few that you don’t like. If you see a photo like this, let us know so | ||
that we can try to take it down - but be aware that it is difficult to | ||
completely erase a photo from all media channels. | ||
|
||
- Be kind and considerate to your fellow hackers and our volunteers. | ||
We're all here to have fun! By participating in the hackathon, you agree | ||
to abide by this [Code of Conduct](https://hackcodeofconduct.org/). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you make a HaC copy of hackcodeofconduct.org so that we don't have the 'make your own' buttons and have our branding on it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure thing, will also add it to the constitution PR. |
||
""" | ||
) | ||
} | ||
|
||
func GameGigCardsContainer(content: Nodeable) -> Node { | ||
return El.Div[Attr.className => "GameGigCardsContainer"].containing( | ||
content | ||
) | ||
} | ||
|
||
/** | ||
Not currently used but may be useful for future events | ||
*/ | ||
func NavBar(elements: [(String, Nodeable)]) -> Node { | ||
return El.Div[Attr.className => "GameGigNavBar"].containing( | ||
El.Span[Attr.className => "GameGigNavBar__poweredby"].containing( | ||
"Powered by ", | ||
GoboLogo, | ||
" and ", | ||
ElectricSquareLogo | ||
), | ||
Fragment(elements.map{ title, content in | ||
El.Span[Attr.className => "GameGigNavBar__item"].containing( | ||
El.A[ | ||
Attr.href => "#\(title.idMangle())" | ||
].containing( | ||
title | ||
) | ||
) | ||
}), | ||
El.Span[Attr.className => "GameGigNavBar__time"].containing(CurrentTime()) | ||
) | ||
} | ||
|
||
// Convert a given time in String format, (eg. "12:00"), to the corresponding | ||
// Swift Date object representing that time on the day of the event itself | ||
let eventDate = Date.from(year: 2017, month: 12, day: 1, hour: 0, minute: 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably clearer to move this into the function itself. It's a little confusing as it's named eventDate but is not the startDate of the game gig (since game gig doesn't start at midnight) so don't want it used elsewhere accidentally |
||
func gameGigDate(_ time: String) -> Date { | ||
// To the dirty work by converting the events date to a string, appending the time, and convertin to a date again | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🙈 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could have been nicer just to do:
;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why we do these code reviews! I spent way too long thinking about this :/ |
||
let outFormatter = DateFormatter() | ||
outFormatter.dateFormat = "yyyy-MM-dd" | ||
outFormatter.timeZone = TimeZone(identifier: "Europe/London") | ||
outFormatter.locale = Locale(identifier: "en_GB") | ||
|
||
let dateTimeString = outFormatter.string(from: eventDate) + " " + time | ||
|
||
let formatter = DateFormatter() | ||
formatter.dateFormat = "yyyy-MM-dd HH:mm" | ||
formatter.timeZone = TimeZone(identifier: "Europe/London") | ||
formatter.locale = Locale(identifier: "en_GB") | ||
let dateTime = formatter.date(from: dateTimeString) | ||
return dateTime ?? Date() | ||
} | ||
|
||
var node: Node { | ||
// Define all time related info | ||
let gigStartDate = gameGigDate("10:30") | ||
let gigEndDate = gameGigDate("20:30") | ||
|
||
|
||
let schedule = [ | ||
("Arrival", gameGigDate("10:00")), | ||
("Start Jamming!", gigStartDate), | ||
("Lunch", gameGigDate("12:00")), | ||
("Dinner", gameGigDate("18:00")), | ||
("Stop Jamming!", gigEndDate), | ||
("LT1 Prizes and wrap-up", gameGigDate("21:00")) | ||
] | ||
|
||
// Define the list of game gig "cards" that are displayed as content | ||
let gameGigCards = [ | ||
("Schedule", Schedule(schedule: schedule)), | ||
("Get Involved", ListOfLinks(dict: socialMediaLinks)), | ||
("Tutorials", ListOfLinks(dict: tutorials)), | ||
("Game Engines", ListOfLinks(dict: gameEngines)), | ||
//("Feed", GameGigTwitterFeed()), | ||
("Rules", Rules()) | ||
] | ||
|
||
return UI.Pages.base( | ||
title: "Hackers at Cambridge Game Gig 80's", | ||
customStylesheets: ["gamegig2017"], | ||
content: Fragment( | ||
El.Div[Attr.className => "GameGigHero"].containing( | ||
El.Img[Attr.className => "GameGigHero__image", Attr.src => Assets.publicPath("/images/gamegig3000/gamegig-foreground.png")] | ||
), | ||
El.Div[Attr.className => "GameGigTopBar"].containing( | ||
ElectricSquareLogo[Attr.className => "GameGigTopBar__image GameGigTopBar__electricSquare"], | ||
El.Div[Attr.className => "GameGigTopBar__filler"], | ||
GoboLogo[Attr.className => "GameGigTopBar__image GameGigTopBar__gobo"] | ||
), | ||
CountDownTimer(startDate: gigStartDate, endDate: gigEndDate), | ||
GameGigCardsContainer(content: Fragment( | ||
gameGigCards.map{ title, content in | ||
GameGigCard(title: title, content: content) | ||
} | ||
)) | ||
) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import HaCTML | ||
|
||
public protocol Hackathon: Nodeable { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because in the future we will have multiple hackathons. TBH I created this when I wasn't sure if there was going to be a set format or not. Shuold I remove? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would maybe be useful if there was any specific data that we needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think this will become useful once we have properly defined event streams, so I will leave it in. |
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import HaCTML | ||
import Foundation | ||
|
||
// TODO: add the ability to highlight the item in the schedule best on the time! | ||
struct Schedule : Nodeable { | ||
let schedule: [(String, Date)] | ||
|
||
var node: Node { | ||
let formatter = DateFormatter() | ||
formatter.dateFormat = "HH:mm" | ||
formatter.timeZone = TimeZone(identifier: "Europe/London") | ||
formatter.locale = Locale(identifier: "en_GB") | ||
return El.Ul.containing( | ||
schedule.map {event, date in | ||
let timeString = formatter.string(from: date) | ||
return El.Li.containing( | ||
timeString, | ||
" ", | ||
event | ||
) | ||
} | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's be consistent with semicolons
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth noting that we're going semicolonless in our gulpfile, following this standard
(up for discussion on this, can probably wait until later)