From f1c4759e0dd6024527b665a965eb1f606e867be0 Mon Sep 17 00:00:00 2001 From: tyler Date: Thu, 14 Dec 2023 16:18:36 -0500 Subject: [PATCH] Implemented followers list --- .gitignore | 2 + NOTES.md | 13 +- app.go | 14 +++ frontend/src/assets/icons/eye-slash.png | Bin 0 -> 4844 bytes frontend/src/assets/icons/eye.png | Bin 0 -> 4640 bytes frontend/src/assets/icons/index.jsx | 5 + frontend/src/screens/Dashboard.css | 76 +++++++++++- frontend/src/screens/Dashboard.jsx | 152 +++++++++++++++++++++++- frontend/src/screens/SignIn.css | 33 ++++- frontend/src/screens/SignIn.jsx | 11 +- go.mod | 3 + go.sum | 6 + 12 files changed, 307 insertions(+), 8 deletions(-) create mode 100644 frontend/src/assets/icons/eye-slash.png create mode 100644 frontend/src/assets/icons/eye.png create mode 100644 frontend/src/assets/icons/index.jsx diff --git a/.gitignore b/.gitignore index 422b4ba..9f51806 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ node_modules frontend/dist frontend/wailsjs + +.prettierignore \ No newline at end of file diff --git a/NOTES.md b/NOTES.md index 6b36ea5..9b49b98 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,5 +1,16 @@ # Doing +Create loading indicator before API is called + +If api query returns error: +- stop interval +- show error to user +- wait for user to press "retry" button to restart interval + +Settings +- allow user to change api key +- allow user to change api interval time + Get user's: username, password, stream key Query API Display followers, subscribers, etc. @@ -7,4 +18,4 @@ Display followers, subscribers, etc. User settings: - API query timer (default: 2s) -# To Do \ No newline at end of file +# To Do diff --git a/app.go b/app.go index 9b538a0..d5d9339 100644 --- a/app.go +++ b/app.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/tylertravisty/go-utils/random" + rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go" ) // App struct @@ -33,3 +34,16 @@ func (a *App) Greet(name string) string { //return fmt.Sprintf("Hello %s, It's show time!", name) return fmt.Sprintf("Hello %s, It's show time!", random) } + +func (a *App) QueryAPI(url string) (*rumblelivestreamlib.Followers, error) { + fmt.Println("QueryAPI") + client := rumblelivestreamlib.Client{StreamKey: url} + resp, err := client.Request() + if err != nil { + // TODO: log error + fmt.Println("client.Request err:", err) + return nil, fmt.Errorf("API request failed") + } + + return &resp.Followers, nil +} diff --git a/frontend/src/assets/icons/eye-slash.png b/frontend/src/assets/icons/eye-slash.png new file mode 100644 index 0000000000000000000000000000000000000000..fe97fa0f57640713589665f97ad0a0f6a55d7091 GIT binary patch literal 4844 zcmbuD`9Bj5{KpZsxnj;7VeYFe$xW6qbLT!|NRF{6cZ%kiD-5|49m+X`<%qfF9Ob@` zg|LV|%anZk{tMsl53l$0@p%3Ae!m`%*AH*3m4z_}s}L�|SQ%-0=24mi?~)=l{)A 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 z z5A~iI+d`?r-t*Ti&nupP!3^p*&wevtHag&kkpfznr`U5i-ilfKopzFN*I;|pRTe0({e z^jA0E;GQgiSwZ^L`w7oe3;wtp2<&n?X9k&32^p1`zaA72l*lm}lvgSc@#w&?Ei`C` zZ_`1xd0z0Ykb7i=uC>8$@c7sVsb4UYyqPae>6GOQescZien7G1t`wlPIf~fWm zvB&<|Tu- zj&gWE={KqH%8ChI4*t5}OiWZPCbk>5R`IeS6{S}Sx=&@tdlfrFR!fUf_^&}byDFVx z?Kf*X{D18uCRW!37XbaD017)^Bdq#E1SF%*z}-yz2|U4dns+X0QWc;R_R6Jb#!IbU zXb|^#qc~BEox}SG?86vk68>FK+pmJ*w^B=_-u9*fGWCMnHlC=1Xd(^%(S&@zXS$!6 z_Io2%@q+qd?~hn=W~{JJh?hfQNa&9W1v~%~`a35iX!Q|@`3oUd{(^eoP`f#Nx-YMw z3WaO!W8c}MrS@n6lK03%k*E)Qz3t^jM=TH223##%?olti90!jt69Sb~s(3Mv?(>AT zcEuS~2?>T|)$d96$7=Jn*b=WYt|)11tjOPk68$by5Hs>pZnpmu=>C7Y&lTN?VU?(> Vm(-h4x%fN)%%PSh^~Q+!{{t5&f3^Ss literal 0 HcmV?d00001 diff --git a/frontend/src/assets/icons/index.jsx b/frontend/src/assets/icons/index.jsx new file mode 100644 index 0000000..c4c8028 --- /dev/null +++ b/frontend/src/assets/icons/index.jsx @@ -0,0 +1,5 @@ +import eye from './eye.png'; +import eye_slash from './eye-slash.png'; + +export const Eye = eye; +export const EyeSlash = eye_slash; diff --git a/frontend/src/screens/Dashboard.css b/frontend/src/screens/Dashboard.css index c8d96c9..fba8777 100644 --- a/frontend/src/screens/Dashboard.css +++ b/frontend/src/screens/Dashboard.css @@ -3,6 +3,80 @@ background-color: #f3f5f8; display: flex; flex-direction: column; - justify-content: space-between; height: 100vh; + width: 100%; +} + +.followers { + background-color: #061726; + height: 100%; + width: 100%; +} + +.followers-list { + overflow-y: auto; +} + +.followers-list-follower { + border-bottom: 1px solid #82b1ff; + color: white; + display: flex; + flex-direction: row; + font-family: sans-serif; + justify-content: space-between; + padding: 10px 20px; +} + +.followers-list-follower-username { + font-weight: bold; +} + +.followers-list-follower-date { + font-family: monospace; +} + +.followers-header { + align-items: center; + border-bottom: 1px solid #82b1ff; + display: flex; + flex-direction: column; + justify-content: space-evenly; + padding: 10px 20px; +} + +.followers-header-title { + color: white; + font-family: sans-serif; + font-size: 24px; + font-weight: bold; + padding-bottom: 10px; + text-transform: uppercase; +} + +.followers-header-highlights { + display: flex; + flex-direction: row; + justify-content: space-evenly; + width: 100%; +} + +.followers-header-highlight { + align-items: center; + color: white; + display: flex; + background-color: #75a54b; + border-radius: 0.5rem; + flex-direction: column; + font-family: sans-serif; + font-weight: bold; + min-width: 50px; + padding: 10px; +} + +.followers-header-highlight-count { + font-size: 16px; +} + +.followers-header-highlight-description { + font-size: 12px; } \ No newline at end of file diff --git a/frontend/src/screens/Dashboard.jsx b/frontend/src/screens/Dashboard.jsx index 5a45a4e..599e0e6 100644 --- a/frontend/src/screens/Dashboard.jsx +++ b/frontend/src/screens/Dashboard.jsx @@ -1,14 +1,162 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { QueryAPI } from '../../wailsjs/go/main/App'; import './Dashboard.css'; function Dashboard() { const location = useLocation(); const [streamKey, setStreamKey] = useState(location.state.streamKey); + const [followers, setFollowers] = useState({}); + const [totalFollowers, setTotalFollowers] = useState('-'); + const [channelFollowers, setChannelFollowers] = useState('-'); + const [latestFollower, setLatestFollower] = useState('-'); + const [recentFollowers, setRecentFollowers] = useState([]); + + // useEffect(() => { + // QueryAPI(streamKey) + // .then((response) => { + // console.log(response); + // setFollowers(response); + // setChannelFollowers(response.num_followers); + // setTotalFollowers(response.num_followers_total); + // setLatestFollower(response.latest_follower.username); + // setRecentFollowers(response.recent_followers); + // }) + // .catch((e) => console.log('Error:', e)); + // }, []); + + useEffect(() => { + let interval = setInterval(() => { + console.log('Query API'); + QueryAPI(streamKey) + .then((response) => { + console.log(response); + setFollowers(response); + setChannelFollowers(response.num_followers); + setTotalFollowers(response.num_followers_total); + setLatestFollower(response.latest_follower.username); + setRecentFollowers(response.recent_followers); + }) + .catch((e) => console.log('Error:', e)); + }, 10000); + + return () => { + clearInterval(interval); + }; + }, []); + + const dateDate = (date) => { + const options = { month: 'short' }; + let month = new Intl.DateTimeFormat('en-US', options).format(date); + let day = date.getDay(); + return month + ' ' + day; + }; + + const dateDay = (date) => { + let now = new Date(); + let today = now.getDay(); + switch (date.getDay()) { + case 0: + return 'Sunday'; + case 1: + return 'Monday'; + case 2: + return 'Tuesday'; + case 3: + return 'Wednesday'; + case 4: + return 'Thursday'; + case 5: + return 'Friday'; + case 6: + return 'Saturday'; + } + }; + + const dateTime = (date) => { + let now = new Date(); + let today = now.getDay(); + let day = date.getDay(); + + if (today !== day) { + return dateDay(date); + } + + let hours24 = date.getHours(); + let hours = hours24 % 12 || 12; + + let minutes = date.getMinutes(); + + let mer = 'pm'; + if (hours24 < 12) { + mer = 'am'; + } + + return hours + ':' + minutes + ' ' + mer; + }; + + const dateString = (d) => { + let now = new Date(); + let date = new Date(d); + // Fix Rumble's timezone problem + date.setHours(date.getHours() - 4); + let diff = now - date; + switch (true) { + case diff < 60000: + return 'Now'; + case diff < 3600000: + let minutes = Math.floor(diff / 1000 / 60); + let postfix = ' minutes ago'; + if (minutes == 1) { + postfix = ' minute ago'; + } + return minutes + postfix; + case diff < 86400000: + return dateTime(date); + case diff < 604800000: + return dateDay(date); + default: + return dateDate(date); + } + console.log('Diff:', diff); + return d; + }; + return (
- Dashboard: {streamKey} + Dashboard: +
+
+ Followers +
+
+ + {channelFollowers} + + Channel +
+
+ + {totalFollowers} + + Total +
+
+
+
+ {recentFollowers.map((follower, index) => ( +
+ + {follower.username} + + + {dateString(follower.followed_on)} + +
+ ))} +
+
); } diff --git a/frontend/src/screens/SignIn.css b/frontend/src/screens/SignIn.css index b4503eb..961bfa8 100644 --- a/frontend/src/screens/SignIn.css +++ b/frontend/src/screens/SignIn.css @@ -23,13 +23,19 @@ } .signin-input { - border: 2px solid #D6E0EA; + border-bottom: 2px solid #D6E0EA; + border-left: 2px solid #D6E0EA; + border-right: none; + border-top: 2px solid #D6E0EA; border-radius: 10rem 0rem 0rem 10rem; background-color: white; color: #061726; outline: none; - padding: 0.5rem 1rem; - width: 80%; + padding-bottom: 0.5rem; + padding-left: 1rem; + padding-right: 0; + padding-top: 0.5rem; + width: 70%; } .signin-button { @@ -47,6 +53,27 @@ background-color: #77b23b; } +.signin-show { + display: flex; + align-items: center; + justify-content: center; + background-color: white; + border-bottom: 2px solid #D6E0EA; + border-left: none; + border-right: 2px solid #D6E0EA; + border-top: 2px solid #D6E0EA; + color: #061726; + cursor: pointer; + font-weight: bold; + text-decoration: none; + width: 10%; +} + +.signin-show-icon { + height: 16px; + width: 16px; +} + .signin-label { font-family: sans-serif; font-weight: bold; diff --git a/frontend/src/screens/SignIn.jsx b/frontend/src/screens/SignIn.jsx index ba27b95..6888fc6 100644 --- a/frontend/src/screens/SignIn.jsx +++ b/frontend/src/screens/SignIn.jsx @@ -1,12 +1,15 @@ import { useEffect, useState } from 'react'; import { Navigate, useNavigate } from 'react-router-dom'; import { NavDashboard } from './Navigation'; +import { Eye, EyeSlash } from '../assets/icons'; import './SignIn.css'; function SignIn() { const navigate = useNavigate(); const [streamKey, setStreamKey] = useState(''); const updateStreamKey = (event) => setStreamKey(event.target.value); + const [showStreamKey, setShowStreamKey] = useState(false); + const updateShowStreamKey = () => setShowStreamKey(!showStreamKey); const saveStreamKey = () => { navigate(NavDashboard, { state: { streamKey: streamKey } }); @@ -26,9 +29,15 @@ function SignIn() { className='signin-input' onChange={updateStreamKey} placeholder='Stream Key' - type='text' + type={showStreamKey ? 'text' : 'password'} value={streamKey} /> + diff --git a/go.mod b/go.mod index 2c1616a..f4e2f55 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.19 require ( github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909 + github.com/tylertravisty/rumble-livestream-lib-go v0.0.0-20231213162428-b33f413975bb github.com/wailsapp/wails/v2 v2.7.1 ) @@ -24,6 +25,7 @@ require ( github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/robertkrimen/otto v0.2.1 // indirect github.com/samber/lo v1.38.1 // indirect github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect @@ -35,6 +37,7 @@ require ( golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect + gopkg.in/sourcemap.v1 v1.0.5 // indirect ) // replace github.com/wailsapp/wails/v2 v2.7.1 => /home/tyler/dev/go/pkg/mod diff --git a/go.sum b/go.sum index e714476..8457545 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= +github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -53,6 +55,8 @@ github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQ github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909 h1:xrjIFqzGQXlCrCdMPpW6+SodGFSlrQ3ZNUCr3f5tF1g= github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909/go.mod h1:2W31Jhs9YSy7y500wsCOW0bcamGi9foQV1CKrfvfTxk= +github.com/tylertravisty/rumble-livestream-lib-go v0.0.0-20231213162428-b33f413975bb h1:nqq0eTr8ocCaENdoryAt9T3g5xJgwcXMW7pYAztb1lc= +github.com/tylertravisty/rumble-livestream-lib-go v0.0.0-20231213162428-b33f413975bb/go.mod h1:YrfW5N6xVozOzubzfNNsy+v0MIL2GPi9Kx3mTZ/Q9zI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -89,6 +93,8 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=