A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #142 from icidasset/v2__textile

(V2) Textile

authored by

Steven Vandevelde and committed by
GitHub
269a5ade 33b51eed

+307 -17
+6
src/Applications/Brain.elm
··· 342 342 Just Alien.AuthRemoteStorage -> 343 343 AuthenticationMsg (Authentication.HypaethralDataRetrieved event.data) 344 344 345 + Just Alien.AuthTextile -> 346 + AuthenticationMsg (Authentication.HypaethralDataRetrieved event.data) 347 + 345 348 Just Alien.FabricateSecretKey -> 346 349 AuthenticationMsg Authentication.SecretKeyFabricated 347 350 ··· 416 419 417 420 Just Alien.AuthRemoteStorage -> 418 421 report Alien.AuthRemoteStorage "I couldn't decrypt your data, maybe you used the wrong passphrase?" 422 + 423 + Just Alien.AuthTextile -> 424 + report Alien.AuthTextile "Something went wrong regarding Textile. Maybe Textile isn't running?" 419 425 420 426 Just tag -> 421 427 report tag err
+17
src/Applications/Brain/Authentication.elm
··· 182 182 |> Ports.requestRemoteStorage 183 183 |> Return.commandWithModel model 184 184 185 + Just (Textile { apiOrigin }) -> 186 + [ ( "apiOrigin", Json.string apiOrigin ) 187 + ] 188 + |> Json.object 189 + |> Alien.broadcast Alien.AuthTextile 190 + |> Ports.requestTextile 191 + |> Return.commandWithModel model 192 + 185 193 -- ✋ 186 194 Nothing -> 187 195 return model ··· 248 256 |> Json.object 249 257 |> Alien.broadcast Alien.AuthRemoteStorage 250 258 |> Ports.toRemoteStorage 259 + |> Return.commandWithModel model 260 + 261 + Just (Textile { apiOrigin }) -> 262 + [ ( "apiOrigin", Json.string apiOrigin ) 263 + , ( "data", json ) 264 + ] 265 + |> Json.object 266 + |> Alien.broadcast Alien.AuthTextile 267 + |> Ports.toTextile 251 268 |> Return.commandWithModel model 252 269 253 270 -- ✋
+8 -2
src/Applications/Brain/Ports.elm
··· 1 - port module Brain.Ports exposing (fabricateSecretKey, fromAlien, receiveSearchResults, receiveTags, removeCache, removeIpfs, requestCache, requestIpfs, requestRemoteStorage, requestSearch, requestTags, toCache, toIpfs, toRemoteStorage, toUI, updateSearchIndex) 1 + port module Brain.Ports exposing (fabricateSecretKey, fromAlien, receiveSearchResults, receiveTags, removeCache, removeIpfs, requestCache, requestIpfs, requestRemoteStorage, requestSearch, requestTags, requestTextile, toCache, toIpfs, toRemoteStorage, toTextile, toUI, updateSearchIndex) 2 2 3 3 import Alien 4 4 import Json.Encode as Json ··· 33 33 port requestTags : ContextForTags -> Cmd msg 34 34 35 35 36 + port requestTextile : Alien.Event -> Cmd msg 37 + 38 + 36 39 port toCache : Alien.Event -> Cmd msg 37 40 38 41 42 + port toIpfs : Alien.Event -> Cmd msg 43 + 44 + 39 45 port toRemoteStorage : Alien.Event -> Cmd msg 40 46 41 47 42 - port toIpfs : Alien.Event -> Cmd msg 48 + port toTextile : Alien.Event -> Cmd msg 43 49 44 50 45 51 port toUI : Alien.Event -> Cmd msg
+41 -14
src/Applications/UI/Authentication.elm
··· 13 13 import Css exposing (pct, px, solid, transparent) 14 14 import Html.Events.Extra.Mouse as Mouse 15 15 import Html.Styled as Html exposing (Html, a, button, em, fromUnstyled, img, span, text) 16 - import Html.Styled.Attributes as Attributes exposing (attribute, css, href, placeholder, src, style, title, width) 16 + import Html.Styled.Attributes as Attributes exposing (attribute, css, href, placeholder, src, style, title, value, width) 17 17 import Html.Styled.Events exposing (onClick, onSubmit) 18 18 import Json.Encode 19 19 import Material.Icons.Av as Icons 20 20 import Material.Icons.Navigation as Icons 21 21 import Return3 as Return exposing (..) 22 22 import SHA 23 + import String.Ext as String 23 24 import Svg exposing (Svg) 24 25 import Tachyons.Classes as T 25 26 import UI.Kit ··· 47 48 48 49 type Model 49 50 = Authenticated Method 50 - | InputScreen Method { input : String, placeholder : String, question : String } 51 + | InputScreen Method Question 51 52 | NewEncryptionKeyScreen Method (Maybe String) 52 53 | UpdateEncryptionKeyScreen Method (Maybe String) 53 54 | Unauthenticated 55 + 56 + 57 + type alias Question = 58 + { placeholder : String 59 + , question : String 60 + , value : String 61 + } 54 62 55 63 56 64 initialModel : Url -> Model ··· 120 128 ----------------------------------------- 121 129 -- More Input 122 130 ----------------------------------------- 123 - | AskForInput Method { placeholder : String, question : String } 131 + | AskForInput Method Question 124 132 | Input String 125 133 | ConfirmInput 126 134 ··· 238 246 -- More Input 239 247 ----------------------------------------- 240 248 AskForInput method opts -> 241 - { input = "" 242 - , placeholder = opts.placeholder 249 + { placeholder = opts.placeholder 243 250 , question = opts.question 251 + , value = opts.value 244 252 } 245 253 |> InputScreen method 246 254 |> return ··· 248 256 Input string -> 249 257 case model of 250 258 InputScreen method opts -> 251 - return (InputScreen method { opts | input = string }) 259 + return (InputScreen method { opts | value = string }) 252 260 253 261 m -> 254 262 return m 255 263 256 264 ConfirmInput -> 257 265 case model of 258 - InputScreen method { input } -> 266 + InputScreen (RemoteStorage r) { value } -> 259 267 addReply 260 - (ExternalAuth method input) 268 + (ExternalAuth (RemoteStorage r) value) 261 269 (return model) 262 270 271 + InputScreen (Textile t) { value } -> 272 + { t | apiOrigin = String.chopEnd "/" value } 273 + |> Textile 274 + |> SignIn 275 + |> updateWithModel model 276 + 263 277 _ -> 264 278 return model 279 + 280 + 281 + updateWithModel : Model -> Msg -> Return Model Msg Reply 282 + updateWithModel model msg = 283 + update msg model 265 284 266 285 267 286 hashPassphrase : String -> String ··· 418 437 , choiceButton 419 438 { action = 420 439 AskForInput 421 - (Authentication.RemoteStorage { userAddress = "", token = "" }) 440 + (RemoteStorage { userAddress = "", token = "" }) 422 441 { placeholder = "username@5apps.com" 423 442 , question = "What's your user address?" 443 + , value = "" 424 444 } 425 445 , icon = \_ _ -> Svg.map never UI.Svg.Elements.remoteStorageLogo 426 446 , isLast = False ··· 428 448 , outOfOrder = False 429 449 } 430 450 , choiceButton 431 - { action = Bypass 451 + { action = 452 + AskForInput 453 + (Textile { apiOrigin = "" }) 454 + { placeholder = "http://localhost:40600" 455 + , question = "Where's your Textile API located?" 456 + , value = "http://localhost:40600" 457 + } 432 458 , icon = \_ _ -> Svg.map never UI.Svg.Elements.textileLogo 433 459 , isLast = False 434 460 , label = "Textile" 435 - , outOfOrder = True 461 + , outOfOrder = False 436 462 } 437 463 438 464 -- More options ··· 525 551 -- INPUT SCREEN 526 552 527 553 528 - inputScreen : { question : String, input : String, placeholder : String } -> Html Msg 529 - inputScreen opts = 554 + inputScreen : Question -> Html Msg 555 + inputScreen question = 530 556 slab 531 557 Html.form 532 558 [ onSubmit ConfirmInput ] ··· 534 560 , T.flex_column 535 561 ] 536 562 [ UI.Kit.textFieldAlt 537 - [ placeholder opts.placeholder 563 + [ placeholder question.placeholder 538 564 , Html.Styled.Events.onInput Input 565 + , value question.value 539 566 ] 540 567 , UI.Kit.button 541 568 UI.Kit.Normal
+3
src/Applications/UI/Settings.elm
··· 70 70 Just (RemoteStorage _) -> 71 71 text "on a RemoteStorage server." 72 72 73 + Just (Textile _) -> 74 + text "on Textile." 75 + 73 76 Nothing -> 74 77 text "on nothing, wtf?" 75 78 , lineBreak
+55
src/Javascript/Workers/brain.js
··· 280 280 281 281 282 282 283 + // Textile 284 + // ------- 285 + 286 + let tt 287 + 288 + 289 + function textile() { 290 + if (!tt) { 291 + importScripts("/textile.js") 292 + tt = true 293 + } 294 + } 295 + 296 + 297 + app.ports.requestTextile.subscribe(event => { 298 + const apiOrigin = event.data.apiOrigin 299 + 300 + textile() 301 + 302 + Textile.ensureThread 303 + (apiOrigin) 304 + 305 + .then(_ => Textile.getFile(apiOrigin)) 306 + .then(f => f ? Textile.readFile(apiOrigin, f) : null) 307 + .then(data => { 308 + app.ports.fromAlien.send({ 309 + tag: event.tag, 310 + data: typeof data === "string" ? JSON.parse(data) : data, 311 + error: null 312 + }) 313 + }) 314 + 315 + .catch(reportError(event)) 316 + }) 317 + 318 + 319 + app.ports.toTextile.subscribe(event => { 320 + const apiOrigin = event.data.apiOrigin 321 + const json = JSON.stringify(event.data.data) 322 + 323 + textile() 324 + 325 + Textile.ensureThread 326 + (apiOrigin) 327 + 328 + .then(_ => Textile.getFile(apiOrigin)) 329 + .then(f => f ? Textile.deleteBlock(apiOrigin, f) : null) 330 + .then(_ => Textile.useMill(apiOrigin, json)) 331 + .then(m => Textile.addFileToThread(apiOrigin, m)) 332 + 333 + .catch(reportError(event)) 334 + }) 335 + 336 + 337 + 283 338 // 🔱 284 339 // -- 285 340
+158
src/Javascript/textile.js
··· 1 + // 2 + // Textile 3 + // \ (•◡•) / 4 + // 5 + // Communicating with the Textile HTTP API. 6 + 7 + 8 + const TEXTILE_THREAD = "diffuse" 9 + const TEXTILE_BLOCK = "userdata" 10 + const TEXTILE_SCHEMA = { 11 + name: TEXTILE_THREAD, 12 + pin: true, 13 + mill: "/blob", 14 + plaintext: false 15 + } 16 + 17 + 18 + const Textile = { 19 + activeThreadId: undefined 20 + } 21 + 22 + 23 + 24 + // Blocks 25 + // ------ 26 + 27 + Textile.deleteBlock = (apiOrigin, file) => { 28 + return fetch(apiOrigin + "/api/v0/blocks/" + file.block, { 29 + method: "DELETE" 30 + }) 31 + .then(r => r.json()) 32 + } 33 + 34 + 35 + 36 + // Threads 37 + // ------- 38 + 39 + Textile.ensureThread = (apiOrigin) => { 40 + if (!Textile.activeThreadId) { 41 + return Textile.getThread(apiOrigin) 42 + .then(thread => { 43 + if (thread) return thread 44 + 45 + return Textile 46 + .createSchema(apiOrigin) 47 + .then(schema => Textile.createThread(apiOrigin, schema)) 48 + }) 49 + .then(t => { 50 + Textile.activeThreadId = t.id 51 + return t.id 52 + }) 53 + } 54 + 55 + return Promise.resolve(Textile.activeThreadId) 56 + } 57 + 58 + 59 + Textile.getThread = (apiOrigin) => { 60 + return fetch(apiOrigin + "/api/v0/threads") 61 + .then(r => r.json()) 62 + .then(r => r.items.find(i => i.name === TEXTILE_THREAD)) 63 + } 64 + 65 + 66 + Textile.createThread = (apiOrigin, schema) => { 67 + const headers = new Headers() 68 + headers.append("X-Textile-Args", TEXTILE_THREAD) 69 + headers.append("X-Textile-Opts", "schema=" + schema.hash + ",type=private,sharing=invite_only") 70 + 71 + return fetch(apiOrigin + "/api/v0/threads", { 72 + headers: headers, 73 + method: "POST" 74 + }) 75 + .then(r => r.json()) 76 + } 77 + 78 + 79 + 80 + // Files 81 + // ----- 82 + 83 + Textile.addFileToThread = (apiOrigin, millResult) => { 84 + const headers = new Headers() 85 + headers.append("Content-Type", "application/json") 86 + 87 + const files = {} 88 + files[":single"] = millResult 89 + 90 + return fetch(apiOrigin + "/api/v0/threads/" + Textile.activeThreadId + "/files", { 91 + body: JSON.stringify({ 92 + items: [{ files: files }] 93 + }), 94 + headers: headers, 95 + method: "POST" 96 + }) 97 + .then(r => r.json()) 98 + } 99 + 100 + 101 + Textile.getFile = (apiOrigin) => { 102 + const headers = new Headers() 103 + headers.append("X-Textile-Opts", "thread=" + Textile.activeThreadId) 104 + 105 + return fetch(apiOrigin + "/api/v0/files", { 106 + headers: headers, 107 + method: "GET" 108 + }) 109 + .then(r => r.json()) 110 + .then(r => r.items.find(i => i.files[0].file.name === TEXTILE_BLOCK)) 111 + } 112 + 113 + 114 + Textile.readFile = (apiOrigin, file) => { 115 + const headers = new Headers() 116 + headers.append("X-Textile-Opts", "key=" + file.files[0].file.key) 117 + 118 + return fetch(apiOrigin + "/api/v0/ipfs/cat/" + file.files[0].file.hash, { 119 + headers: headers, 120 + method: "GET" 121 + }) 122 + .then(r => r.text()) 123 + } 124 + 125 + 126 + 127 + // Mills 128 + // ----- 129 + 130 + Textile.createSchema = (apiOrigin) => { 131 + const headers = new Headers() 132 + headers.append("Content-Type", "application/json") 133 + 134 + return fetch(apiOrigin + "/api/v0/mills/schema", { 135 + body: JSON.stringify(TEXTILE_SCHEMA), 136 + headers: headers, 137 + method: "POST" 138 + }) 139 + .then(r => r.json()) 140 + } 141 + 142 + 143 + Textile.useMill = (apiOrigin, data) => { 144 + const blob = new Blob([data], { type : "application/json" }) 145 + const file = new File([blob], TEXTILE_BLOCK) 146 + 147 + const formData = new FormData() 148 + formData.append("file", file) 149 + 150 + const headers = new Headers() 151 + headers.append("X-Textile-Opts", "plaintext=false") 152 + 153 + return fetch(apiOrigin + "/api/v0/mills/blob", { 154 + body: formData, 155 + method: "POST" 156 + }) 157 + .then(r => r.json()) 158 + }
+7
src/Library/Alien.elm
··· 26 26 | AuthMethod 27 27 | AuthRemoteStorage 28 28 | AuthSecretKey 29 + | AuthTextile 29 30 | FabricateSecretKey 30 31 | SearchTracks 31 32 -- from UI ··· 99 100 100 101 AuthSecretKey -> 101 102 "AUTH_SECRET_KEY" 103 + 104 + AuthTextile -> 105 + "AUTH_TEXTILE" 102 106 103 107 FabricateSecretKey -> 104 108 "FABRICATE_SECRET_KEY" ··· 190 194 191 195 "AUTH_SECRET_KEY" -> 192 196 Just AuthSecretKey 197 + 198 + "AUTH_TEXTILE" -> 199 + Just AuthTextile 193 200 194 201 "FABRICATE_SECRET_KEY" -> 195 202 Just FabricateSecretKey
+11
src/Library/Authentication.elm
··· 20 20 = Ipfs 21 21 | Local 22 22 | RemoteStorage { userAddress : String, token : String } 23 + | Textile { apiOrigin : String } 23 24 24 25 25 26 type alias EnclosedUserData = ··· 78 79 , token 79 80 ] 80 81 82 + Textile { apiOrigin } -> 83 + String.join 84 + methodSeparator 85 + [ "TEXTILE" 86 + , apiOrigin 87 + ] 88 + 81 89 82 90 methodFromString : String -> Maybe Method 83 91 methodFromString string = ··· 90 98 91 99 [ "REMOTE_STORAGE", u, t ] -> 92 100 Just (RemoteStorage { userAddress = u, token = t }) 101 + 102 + [ "TEXTILE", a ] -> 103 + Just (Textile { apiOrigin = a }) 93 104 94 105 _ -> 95 106 Nothing
+1 -1
src/Library/Sources/Services/AmazonS3.elm
··· 63 63 } 64 64 , { key = "host" 65 65 , label = "Host (optional)" 66 - , placeholder = "http://127.0.0.1:9000" 66 + , placeholder = "http://localhost:9000" 67 67 , password = False 68 68 } 69 69 ]