├── .gitignore
├── README.md
├── bsconfig.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── s3_website.yml
├── src
├── Member.re
├── MembersData.re
├── MembersList.re
├── app.css
├── app.re
├── event.re
├── footer.re
├── index.css
├── index.re
├── meetup.re
├── noUpcomingEvents.re
└── registerServiceWorker.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # bucklescript
13 | /lib
14 | /types
15 | .merlin
16 |
17 | # misc
18 | .DS_Store
19 | .vscode
20 | .env.local
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 |
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Getting started
2 |
3 | 1. Clone the repo
4 | 2. `yarn install`
5 | 3. `yarn start`
6 | 4. Open browser and navigate to: http://localhost:3000/
7 |
8 | ### Intro
9 |
10 | The project uses create-react-app with [reason-scripts](https://github.com/reasonml-community/reason-scripts)
11 |
12 | ### Ideas for future TODOs
13 |
14 | * Decode Json using https://github.com/BuckleTypes/bs-json
15 | * Boilerplate for all ReasonML meetups (other countries)
16 | * Profile pages for speakers/participants (way to share Code or Talks) - people can PR their profiles into the project
17 | * Fetch YouTube and Meetup data into the page
18 | * Links to all world-wide ReasonML meetups
19 |
--------------------------------------------------------------------------------
/bsconfig.json:
--------------------------------------------------------------------------------
1 | /* This is the BuckleScript configuration file. Note that this is a comment;
2 | BuckleScript comes with a JSON parser that supports comments and trailing
3 | comma. If this screws with your editor highlighting, please tell us by filing
4 | an issue! */
5 | {
6 | "name": "reasonvienna-website",
7 | "reason" : { "react-jsx" : 2},
8 | "refmt": 3,
9 | "bs-dependencies": [
10 | "reason-react",
11 | "bs-fetch",
12 | "bs-json",
13 | "bs-moment"
14 | ],
15 | "sources": [
16 | {
17 | "dir": "src"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "bs-fetch": "github:reasonml-community/bs-fetch",
7 | "bs-json": "^0.2.3",
8 | "bs-moment": "^0.1.4",
9 | "moment": "^2.18.1",
10 | "react": "^15.6.1",
11 | "react-dom": "^15.6.1",
12 | "reason-scripts": "0.6.9"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject",
19 | "prepare": "npm link bs-platform"
20 | },
21 | "devDependencies": {
22 | "bs-jest": "^0.1.0",
23 | "reason-react": "^0.2.4"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReasonVienna/website/0a216ca101bb005188db03a32f172f550b3eabc7/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Reason Vienna
24 |
25 |
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "ReasonVienna",
3 | "name": "Reason Vienna Starter Website",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/s3_website.yml:
--------------------------------------------------------------------------------
1 | s3_id: <%= ENV['S3_ID'] %>
2 | s3_secret: <%= ENV['S3_SECRET'] %>
3 | s3_bucket: reasonml.re
4 |
5 | # Below are examples of all the available configurations.
6 | # See README for more detailed info on each of them.
7 |
8 | site: public
9 |
10 | index_document: index.html
11 | error_document: error.html
12 |
13 | # max_age:
14 | # "assets/*": 6000
15 | # "*": 300
16 |
17 | # gzip:
18 | # - .html
19 | # - .css
20 | # - .md
21 | # gzip_zopfli: true
22 |
23 | # See http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region for valid endpoints
24 | s3_endpoint: eu-west-1
25 |
26 | # ignore_on_server: that_folder_of_stuff_i_dont_keep_locally
27 |
28 | # exclude_from_upload:
29 | # - those_folders_of_stuff
30 | # - i_wouldnt_want_to_upload
31 |
32 | # s3_reduced_redundancy: true
33 |
34 | # cloudfront_distribution_id: your-dist-id
35 |
36 | # cloudfront_distribution_config:
37 | # default_cache_behavior:
38 | # min_TTL: <%= 60 * 60 * 24 %>
39 | # aliases:
40 | # quantity: 1
41 | # items:
42 | # CNAME: your.website.com
43 |
44 | # cloudfront_invalidate_root: true
45 |
46 | # cloudfront_wildcard_invalidation: true
47 |
48 | # concurrency_level: 5
49 |
50 | # redirects:
51 | # index.php: /
52 | # about.php: about.html
53 | # music-files/promo.mp4: http://www.youtube.com/watch?v=dQw4w9WgXcQ
54 |
55 | # routing_rules:
56 | # - condition:
57 | # key_prefix_equals: blog/some_path
58 | # redirect:
59 | # host_name: blog.example.com
60 | # replace_key_prefix_with: some_new_path/
61 | # http_redirect_code: 301
62 |
--------------------------------------------------------------------------------
/src/Member.re:
--------------------------------------------------------------------------------
1 | type member = {
2 | id: int,
3 | login: string,
4 | url: string,
5 | avatar_url: string
6 | };
7 |
8 | let component = ReasonReact.statelessComponent("Member");
9 |
10 | type props = {member};
11 |
12 | let skin =
13 | ReactDOMRe.Style.make(~color="#f6f4f4", ~margin="0 auto", ~height="50px", ~width="50px", ());
14 |
15 | let linkSkin =
16 | ReactDOMRe.Style.make(
17 | ~color="gray",
18 | ~fontFamily="Helvetica Neue, Open Sans, sans-serif",
19 | ~fontWeight="bold",
20 | ~lineHeight="1.6",
21 | ~fontSize="16px",
22 | ~textDecoration="none",
23 | ~margin="2px",
24 | ~textAlign="center",
25 | ()
26 | );
27 |
28 | let make = (~member, _children) => {
29 | ...component,
30 | render: (_self) =>
31 |
38 | };
39 |
--------------------------------------------------------------------------------
/src/MembersData.re:
--------------------------------------------------------------------------------
1 | let clientId = "9547d8b8def885c8e5e3";
2 |
3 | let secret = "529880bee32f6d92515053f31eab2dc77fe68930";
4 |
5 | let param = "?client_id=" ++ (clientId ++ ("&client_secret=" ++ secret));
6 |
7 | let apiUrl = "https://api.github.com/orgs/ReasonVienna/members" ++ param;
8 |
9 | type member = Member.member;
10 |
11 | type members = array(member);
12 |
13 | let parseMember = (json) : member =>
14 | Json.Decode.{
15 | id: json |> field("id", int),
16 | login: json |> field("login", string),
17 | url: json |> field("url", string),
18 | avatar_url: json |> field("avatar_url", string)
19 | };
20 |
21 | let parseMembers = (json) : array(member) => Json.Decode.(json |> array(parseMember));
22 |
23 | let fetchMembers = (callback) =>
24 | Js.Promise.(
25 | Bs_fetch.fetch(apiUrl)
26 | |> then_(Bs_fetch.Response.text)
27 | |> then_(
28 | (text) =>
29 | Js.Json.parseExn(text)
30 | |> parseMembers
31 | |> (
32 | (members) => {
33 | callback(members);
34 | resolve(None)
35 | }
36 | )
37 | )
38 | );
39 |
--------------------------------------------------------------------------------
/src/MembersList.re:
--------------------------------------------------------------------------------
1 | type action =
2 | | Loaded(array(Member.member))
3 | | Loading;
4 |
5 | type state = {
6 | members: array(Member.member),
7 | loading: bool
8 | };
9 |
10 | let component = ReasonReact.reducerComponent("MembersList");
11 |
12 | let se = ReasonReact.stringToElement;
13 |
14 | let loadData = ({ReasonReact.state, reduce}) => {
15 | MembersData.fetchMembers(reduce((payload) => Loaded(payload))) |> ignore;
16 | reduce(() => Loading, ())
17 | };
18 |
19 | let make = (_children) => {
20 | ...component,
21 | initialState: fun () => ({loading: false, members: [||]}: state),
22 | didMount: (self) => {
23 | loadData(self);
24 | ReasonReact.NoUpdate
25 | },
26 | reducer: (action, state) =>
27 | switch action {
28 | | Loading => ReasonReact.Update({...state, loading: true})
29 | | Loaded(data) =>
30 | let updatedMembers = Array.concat([state.members, data]);
31 | ReasonReact.Update({members: updatedMembers, loading: false})
32 | },
33 | render: (self) =>
34 |
35 |
36 | (se("Members"))
37 | (
38 | Array.mapi(
39 | (index, member) => ,
40 | self.state.members
41 | )
42 | |> ReasonReact.arrayToElement
43 | )
44 |
45 | };
46 |
--------------------------------------------------------------------------------
/src/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Open Sans, sans-serif;
3 | background-color: #f6f4f4;
4 | }
5 |
6 | code {
7 | font-family: Courier New;
8 | color: #8f8f8f;
9 | }
10 |
11 | ul {
12 | margin: 0;
13 | padding: 0;
14 | }
15 |
16 | .App {
17 | margin: 50px;
18 | }
19 |
20 | .App-logo {
21 | animation: App-logo-spin infinite 20s linear;
22 | height: 200px;
23 | }
24 |
25 | .App-header {
26 | display: flex;
27 | align-items: center;
28 | margin: 50px 0;
29 | color: #333333;
30 | }
31 |
32 | .App-intro {
33 | font-size: large;
34 | color: #444;
35 | }
36 |
37 | .block {
38 | display: inline-block;
39 | fontSize: '1rem';
40 | fontWeight: '300';
41 | width: '200px';
42 | textAlign: 'center';
43 | margin: 5px;
44 | border: 2px gray;
45 | }
46 |
47 | @keyframes App-logo-spin {
48 | from {
49 | transform: rotate(0deg);
50 | }
51 | to {
52 | transform: rotate(360deg);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/app.re:
--------------------------------------------------------------------------------
1 | type actions =
2 | | UpdateEvents(array(Event.event));
3 |
4 | type state = {
5 | description: string,
6 | events: array(Event.event),
7 | meetups: array(Meetup.reasonMeetup)
8 | };
9 |
10 | let knownMeetups: array(Meetup.reasonMeetup) = [|
11 | {
12 | city: "Chicago",
13 | name: "Chicago ReasonML",
14 | shortName: "CHG",
15 | page: "https://www.meetup.com/Chicago-ReasonML/",
16 | logo: "https://secure.meetupstatic.com/photos/event/3/4/c/b/global_459553515.jpeg"
17 | },
18 | {
19 | city: "New York City",
20 | name: "Reason NYC",
21 | shortName: "NYC",
22 | page: "https://www.meetup.com/ReasonML-NYC/",
23 | logo: "https://secure.meetupstatic.com/photos/event/9/1/2/f/global_461257167.jpeg"
24 | },
25 | {
26 | city: "Sydney",
27 | name: "Reason Sydney",
28 | shortName: "SDY",
29 | page: "https://www.meetup.com/reason-sydney/",
30 | logo: "https://secure.meetupstatic.com/photos/event/c/e/4/c/global_460672812.jpeg"
31 | },
32 | {
33 | city: "Paris",
34 | name: "ReasonML Paris",
35 | shortName: "PAR",
36 | page: "https://www.meetup.com/ReasonML-Paris/",
37 | logo: "https://secure.meetupstatic.com/photos/event/6/4/b/1/global_457585777.jpeg"
38 | },
39 | {
40 | city: "Oslo",
41 | name: "Reason Oslo Meetup",
42 | shortName: "OSL",
43 | page: "https://reasonoslo.xyz/",
44 | logo: "https://reasonoslo.xyz/static/logo.png"
45 | },
46 | {
47 | city: "Montreal",
48 | name: "ReasonMTL Montreal",
49 | shortName: "MTL",
50 | page: "https://www.meetup.com/ReasonMTL/",
51 | logo: "https://secure.meetupstatic.com/photos/event/2/9/3/6/global_461710550.jpeg"
52 | },
53 | {
54 | city: "San Francisco",
55 | name: "Silicon Valley OCaml/Reason",
56 | shortName: "SF",
57 | page: "https://www.meetup.com/sv-ocaml/",
58 | logo: "https://secure.meetupstatic.com/photos/event/2/8/c/a/global_332890442.jpeg"
59 | },
60 | {
61 | city: "London",
62 | name: "ReasonLDN",
63 | shortName: "LDN",
64 | page: "https://www.meetup.com/ReasonLDN/",
65 | logo: "https://secure.meetupstatic.com/photos/event/5/1/7/8/global_461900856.jpeg"
66 | },
67 | {
68 | city: "Singapore",
69 | name: "Singapore OCaml/Reason",
70 | shortName: "SIP",
71 | page: "https://www.meetup.com/SG-OCaml/",
72 | logo: "https://secure.meetupstatic.com/photos/event/2/8/c/a/global_332890442.jpeg"
73 | }
74 | |];
75 |
76 | let upcomingEventsOrWelcomeMessage = (scheduledEvents) =>
77 | switch (Array.length(scheduledEvents)) {
78 | | 0 =>
79 | | _ =>
80 | ReasonReact.arrayToElement(
81 | scheduledEvents
82 | |> Array.mapi((index, event: Event.event) => )
83 | )
84 | };
85 |
86 | let component = ReasonReact.reducerComponent("App");
87 |
88 | let make = (_children) => {
89 | ...component,
90 | initialState: () => {description: "loading...", events: [||], meetups: knownMeetups},
91 | didMount: (self) => {
92 | let changeState = (events) => self.reduce((_) => UpdateEvents(events), ());
93 | let _ =
94 | Js.Promise.(
95 | Bs_fetch.fetch(
96 | "https://crossorigin.me/https://api.meetup.com/Reason-Vienna/events?photo-host=secure&page=20&sig_id=12607916&sig=197d614dc57e10c6ee4c20dbfe9a191caf88a740"
97 | )
98 | |> then_(Bs_fetch.Response.json)
99 | |> then_((result) => Event.parse(result) |> changeState |> resolve)
100 | );
101 | ReasonReact.NoUpdate
102 | },
103 | reducer: (action, state) =>
104 | switch action {
105 | | UpdateEvents(events) => ReasonReact.Update({...state, events})
106 | },
107 | render: ({state}) =>
108 |
109 |
117 |
118 |
120 |
121 |
122 |
125 |
126 |
127 |
128 |
129 |
133 |
137 |
138 |
139 |
140 |
141 |
142 | (ReasonReact.stringToElement("Reason Vienna"))
143 |
144 |
145 | (ReasonReact.stringToElement(state.description))
146 |
147 |
148 |
(upcomingEventsOrWelcomeMessage(state.events))
149 |
150 |
151 |
152 |
153 | };
154 |
--------------------------------------------------------------------------------
/src/event.re:
--------------------------------------------------------------------------------
1 | type event = {
2 | id: string,
3 | title: string,
4 | description: string,
5 | time: float
6 | };
7 |
8 | let component = ReasonReact.statelessComponent("Event");
9 |
10 | type props = {event};
11 |
12 | module Decode = {
13 | let event = (j) =>
14 | Json.Decode.{
15 | id: j |> field("id", string),
16 | title: j |> field("name", string),
17 | description: j |> field("description", string),
18 | time: j |> field("time", float)
19 | };
20 | let root = (json) => Json.Decode.(array(event, json));
21 | };
22 |
23 | let parse = (json: Js.Json.t) => Decode.root(json);
24 |
25 | let make = (~event, _children) => {
26 | ...component,
27 | render: (_self) => {
28 | let meetupTime = event.time |> Js.Date.fromFloat |> Js.Date.toISOString |> MomentRe.moment;
29 |
30 |
(ReasonReact.stringToElement("When? "))
31 |
44 | (ReasonReact.stringToElement(MomentRe.Moment.format("dddd, MMMM D, YYYY", meetupTime)))
45 |
46 | (ReasonReact.stringToElement(MomentRe.Moment.format("H:mm", meetupTime)))
47 |
48 |
(ReasonReact.stringToElement(event.title))
49 |
50 |
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/footer.re:
--------------------------------------------------------------------------------
1 | let component = ReasonReact.statelessComponent("Footer");
2 |
3 | type props = {meetups: array(Meetup.reasonMeetup)};
4 |
5 | let footerSkin = ReactDOMRe.Style.make(~padding="10px", ~borderTop="10px", ~width="100%", ());
6 |
7 | let make = (~meetups, _children) => {
8 | ...component,
9 | render: (_self) => {
10 | let ms =
11 | meetups
12 | |> Array.mapi((index, meetup) => );
13 |
14 |
15 |
(ReasonReact.stringToElement("Meetups around the World"))
16 |
(ReasonReact.arrayToElement(ms))
17 |
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /* Document
4 | ========================================================================== */
5 |
6 | /**
7 | * 1. Correct the line height in all browsers.
8 | * 2. Prevent adjustments of font size after orientation changes in
9 | * IE on Windows Phone and in iOS.
10 | */
11 |
12 | html {
13 | line-height: 1.15; /* 1 */
14 | -ms-text-size-adjust: 100%; /* 2 */
15 | -webkit-text-size-adjust: 100%; /* 2 */
16 | }
17 |
18 | /* Sections
19 | ========================================================================== */
20 |
21 | /**
22 | * Remove the margin in all browsers (opinionated).
23 | */
24 |
25 | body {
26 | margin: 0;
27 | }
28 |
29 | /**
30 | * Add the correct display in IE 9-.
31 | */
32 |
33 | article,
34 | aside,
35 | footer,
36 | header,
37 | nav,
38 | section {
39 | display: block;
40 | }
41 |
42 | /**
43 | * Correct the font size and margin on `h1` elements within `section` and
44 | * `article` contexts in Chrome, Firefox, and Safari.
45 | */
46 |
47 | h1 {
48 | font-size: 2em;
49 | margin: 0.67em 0;
50 | }
51 |
52 | /* Grouping content
53 | ========================================================================== */
54 |
55 | /**
56 | * Add the correct display in IE 9-.
57 | * 1. Add the correct display in IE.
58 | */
59 |
60 | figcaption,
61 | figure,
62 | main {
63 | /* 1 */
64 | display: block;
65 | }
66 |
67 | /**
68 | * Add the correct margin in IE 8.
69 | */
70 |
71 | figure {
72 | margin: 1em 40px;
73 | }
74 |
75 | /**
76 | * 1. Add the correct box sizing in Firefox.
77 | * 2. Show the overflow in Edge and IE.
78 | */
79 |
80 | hr {
81 | box-sizing: content-box; /* 1 */
82 | height: 0; /* 1 */
83 | overflow: visible; /* 2 */
84 | }
85 |
86 | /**
87 | * 1. Correct the inheritance and scaling of font size in all browsers.
88 | * 2. Correct the odd `em` font sizing in all browsers.
89 | */
90 |
91 | pre {
92 | font-family: monospace, monospace; /* 1 */
93 | font-size: 1em; /* 2 */
94 | }
95 |
96 | /* Text-level semantics
97 | ========================================================================== */
98 |
99 | /**
100 | * 1. Remove the gray background on active links in IE 10.
101 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
102 | */
103 |
104 | a {
105 | background-color: transparent; /* 1 */
106 | -webkit-text-decoration-skip: objects; /* 2 */
107 | }
108 |
109 | /**
110 | * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
111 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
112 | */
113 |
114 | abbr[title] {
115 | border-bottom: none; /* 1 */
116 | text-decoration: underline; /* 2 */
117 | text-decoration: underline dotted; /* 2 */
118 | }
119 |
120 | /**
121 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
122 | */
123 |
124 | b,
125 | strong {
126 | font-weight: inherit;
127 | }
128 |
129 | /**
130 | * Add the correct font weight in Chrome, Edge, and Safari.
131 | */
132 |
133 | b,
134 | strong {
135 | font-weight: bolder;
136 | }
137 |
138 | /**
139 | * 1. Correct the inheritance and scaling of font size in all browsers.
140 | * 2. Correct the odd `em` font sizing in all browsers.
141 | */
142 |
143 | code,
144 | kbd,
145 | samp {
146 | font-family: monospace, monospace; /* 1 */
147 | font-size: 1em; /* 2 */
148 | }
149 |
150 | /**
151 | * Add the correct font style in Android 4.3-.
152 | */
153 |
154 | dfn {
155 | font-style: italic;
156 | }
157 |
158 | /**
159 | * Add the correct background and color in IE 9-.
160 | */
161 |
162 | mark {
163 | background-color: #ff0;
164 | color: #000;
165 | }
166 |
167 | /**
168 | * Add the correct font size in all browsers.
169 | */
170 |
171 | small {
172 | font-size: 80%;
173 | }
174 |
175 | /**
176 | * Prevent `sub` and `sup` elements from affecting the line height in
177 | * all browsers.
178 | */
179 |
180 | sub,
181 | sup {
182 | font-size: 75%;
183 | line-height: 0;
184 | position: relative;
185 | vertical-align: baseline;
186 | }
187 |
188 | sub {
189 | bottom: -0.25em;
190 | }
191 |
192 | sup {
193 | top: -0.5em;
194 | }
195 |
196 | /* Embedded content
197 | ========================================================================== */
198 |
199 | /**
200 | * Add the correct display in IE 9-.
201 | */
202 |
203 | audio,
204 | video {
205 | display: inline-block;
206 | }
207 |
208 | /**
209 | * Add the correct display in iOS 4-7.
210 | */
211 |
212 | audio:not([controls]) {
213 | display: none;
214 | height: 0;
215 | }
216 |
217 | /**
218 | * Remove the border on images inside links in IE 10-.
219 | */
220 |
221 | img {
222 | border-style: none;
223 | }
224 |
225 | /**
226 | * Hide the overflow in IE.
227 | */
228 |
229 | svg:not(:root) {
230 | overflow: hidden;
231 | }
232 |
233 | /* Forms
234 | ========================================================================== */
235 |
236 | /**
237 | * 1. Change the font styles in all browsers (opinionated).
238 | * 2. Remove the margin in Firefox and Safari.
239 | */
240 |
241 | button,
242 | input,
243 | optgroup,
244 | select,
245 | textarea {
246 | font-family: sans-serif; /* 1 */
247 | font-size: 100%; /* 1 */
248 | line-height: 1.15; /* 1 */
249 | margin: 0; /* 2 */
250 | }
251 |
252 | /**
253 | * Show the overflow in IE.
254 | * 1. Show the overflow in Edge.
255 | */
256 |
257 | button,
258 | input {
259 | /* 1 */
260 | overflow: visible;
261 | }
262 |
263 | /**
264 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
265 | * 1. Remove the inheritance of text transform in Firefox.
266 | */
267 |
268 | button,
269 | select {
270 | /* 1 */
271 | text-transform: none;
272 | }
273 |
274 | /**
275 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
276 | * controls in Android 4.
277 | * 2. Correct the inability to style clickable types in iOS and Safari.
278 | */
279 |
280 | button,
281 | html [type='button'],
282 | /* 1 */ [type='reset'],
283 | [type='submit'] {
284 | -webkit-appearance: button; /* 2 */
285 | }
286 |
287 | /**
288 | * Remove the inner border and padding in Firefox.
289 | */
290 |
291 | button::-moz-focus-inner,
292 | [type='button']::-moz-focus-inner,
293 | [type='reset']::-moz-focus-inner,
294 | [type='submit']::-moz-focus-inner {
295 | border-style: none;
296 | padding: 0;
297 | }
298 |
299 | /**
300 | * Restore the focus styles unset by the previous rule.
301 | */
302 |
303 | button:-moz-focusring,
304 | [type='button']:-moz-focusring,
305 | [type='reset']:-moz-focusring,
306 | [type='submit']:-moz-focusring {
307 | outline: 1px dotted ButtonText;
308 | }
309 |
310 | /**
311 | * Correct the padding in Firefox.
312 | */
313 |
314 | fieldset {
315 | padding: 0.35em 0.75em 0.625em;
316 | }
317 |
318 | /**
319 | * 1. Correct the text wrapping in Edge and IE.
320 | * 2. Correct the color inheritance from `fieldset` elements in IE.
321 | * 3. Remove the padding so developers are not caught out when they zero out
322 | * `fieldset` elements in all browsers.
323 | */
324 |
325 | legend {
326 | box-sizing: border-box; /* 1 */
327 | color: inherit; /* 2 */
328 | display: table; /* 1 */
329 | max-width: 100%; /* 1 */
330 | padding: 0; /* 3 */
331 | white-space: normal; /* 1 */
332 | }
333 |
334 | /**
335 | * 1. Add the correct display in IE 9-.
336 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
337 | */
338 |
339 | progress {
340 | display: inline-block; /* 1 */
341 | vertical-align: baseline; /* 2 */
342 | }
343 |
344 | /**
345 | * Remove the default vertical scrollbar in IE.
346 | */
347 |
348 | textarea {
349 | overflow: auto;
350 | }
351 |
352 | /**
353 | * 1. Add the correct box sizing in IE 10-.
354 | * 2. Remove the padding in IE 10-.
355 | */
356 |
357 | [type='checkbox'],
358 | [type='radio'] {
359 | box-sizing: border-box; /* 1 */
360 | padding: 0; /* 2 */
361 | }
362 |
363 | /**
364 | * Correct the cursor style of increment and decrement buttons in Chrome.
365 | */
366 |
367 | [type='number']::-webkit-inner-spin-button,
368 | [type='number']::-webkit-outer-spin-button {
369 | height: auto;
370 | }
371 |
372 | /**
373 | * 1. Correct the odd appearance in Chrome and Safari.
374 | * 2. Correct the outline style in Safari.
375 | */
376 |
377 | [type='search'] {
378 | -webkit-appearance: textfield; /* 1 */
379 | outline-offset: -2px; /* 2 */
380 | }
381 |
382 | /**
383 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
384 | */
385 |
386 | [type='search']::-webkit-search-cancel-button,
387 | [type='search']::-webkit-search-decoration {
388 | -webkit-appearance: none;
389 | }
390 |
391 | /**
392 | * 1. Correct the inability to style clickable types in iOS and Safari.
393 | * 2. Change font properties to `inherit` in Safari.
394 | */
395 |
396 | ::-webkit-file-upload-button {
397 | -webkit-appearance: button; /* 1 */
398 | font: inherit; /* 2 */
399 | }
400 |
401 | /* Interactive
402 | ========================================================================== */
403 |
404 | /*
405 | * Add the correct display in IE 9-.
406 | * 1. Add the correct display in Edge, IE, and Firefox.
407 | */
408 |
409 | details,
410 | /* 1 */ menu {
411 | display: block;
412 | }
413 |
414 | /*
415 | * Add the correct display in all browsers.
416 | */
417 |
418 | summary {
419 | display: list-item;
420 | }
421 |
422 | /* Scripting
423 | ========================================================================== */
424 |
425 | /**
426 | * Add the correct display in IE 9-.
427 | */
428 |
429 | canvas {
430 | display: inline-block;
431 | }
432 |
433 | /**
434 | * Add the correct display in IE.
435 | */
436 |
437 | template {
438 | display: none;
439 | }
440 |
441 | /* Hidden
442 | ========================================================================== */
443 |
444 | /**
445 | * Add the correct display in IE 10-.
446 | */
447 |
448 | [hidden] {
449 | display: none;
450 | }
451 |
--------------------------------------------------------------------------------
/src/index.re:
--------------------------------------------------------------------------------
1 | /* ReactDOMRe.renderToElementWithId "root"; */
2 | [%bs.raw {|require('./index.css')|}];
3 |
4 | [%bs.raw {|require('./app.css')|}];
5 |
6 | [@bs.module "./registerServiceWorker"] external register_service_worker : unit => unit = "default";
7 |
8 | ReactDOMRe.renderToElementWithId( , "root");
9 |
10 | register_service_worker();
11 |
--------------------------------------------------------------------------------
/src/meetup.re:
--------------------------------------------------------------------------------
1 | type reasonMeetup = {
2 | city: string,
3 | name: string,
4 | shortName: string,
5 | page: string,
6 | logo: string
7 | };
8 |
9 | let component = ReasonReact.statelessComponent("Meetup");
10 |
11 | type props = {meetup: reasonMeetup};
12 |
13 | let skin =
14 | ReactDOMRe.Style.make(~color="#f6f4f4", ~margin="0 auto", ~height="50px", ~width="50px", ());
15 |
16 | let linkSkin =
17 | ReactDOMRe.Style.make(
18 | ~color="gray",
19 | ~fontFamily="Helvetica Neue, Open Sans, sans-serif",
20 | ~fontWeight="bold",
21 | ~lineHeight="1.6",
22 | ~fontSize="16px",
23 | ~textDecoration="none",
24 | ~margin="2px",
25 | ~textAlign="center",
26 | ()
27 | );
28 |
29 | let make = (~reasonMeetup, _children) => {
30 | ...component,
31 | render: (_self) =>
32 |
33 |
34 |
39 |
40 | };
41 |
--------------------------------------------------------------------------------
/src/noUpcomingEvents.re:
--------------------------------------------------------------------------------
1 | let component = ReasonReact.statelessComponent("NoUpcomingEvents");
2 |
3 | let make = (_children) => {
4 | ...component,
5 | render: (_self) =>
6 |
7 |
(ReasonReact.stringToElement("Welcome to the ReasonML Vienna Meetup group"))
8 |
9 | (
10 | ReasonReact.stringToElement(
11 | "If you are interested in ReasonML feel free to join our meetup page. At the moment there are no events scheduled. Check back soon to find out about upcoming meetups or hacksessions!"
12 | )
13 | )
14 |
15 |
16 | };
17 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------