Add support for business and gov verification
Also improve icon rendering on Firefox
This commit is contained in:
parent
d6be08d093
commit
f8254c2f0f
|
@ -29,12 +29,10 @@ const
|
||||||
"include_cards": "1",
|
"include_cards": "1",
|
||||||
"include_entities": "1",
|
"include_entities": "1",
|
||||||
"include_profile_interstitial_type": "0",
|
"include_profile_interstitial_type": "0",
|
||||||
"include_quote_count": "1",
|
"include_quote_count": "0",
|
||||||
"include_reply_count": "1",
|
"include_reply_count": "0",
|
||||||
"include_user_entities": "1",
|
"include_user_entities": "0",
|
||||||
"include_ext_reply_count": "1",
|
"include_ext_reply_count": "0",
|
||||||
"include_ext_is_blue_verified": "1",
|
|
||||||
# "include_ext_verified_type": "1",
|
|
||||||
"include_ext_media_color": "0",
|
"include_ext_media_color": "0",
|
||||||
"cards_platform": "Web-13",
|
"cards_platform": "Web-13",
|
||||||
"tweet_mode": "extended",
|
"tweet_mode": "extended",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import options
|
import options
|
||||||
import jsony
|
import jsony
|
||||||
import user, ../types/[graphuser, graphlistmembers]
|
import user, ../types/[graphuser, graphlistmembers]
|
||||||
from ../../types import User, Result, Query, QueryKind
|
from ../../types import User, VerifiedType, Result, Query, QueryKind
|
||||||
|
|
||||||
proc parseGraphUser*(json: string): User =
|
proc parseGraphUser*(json: string): User =
|
||||||
if json.len == 0 or json[0] != '{':
|
if json.len == 0 or json[0] != '{':
|
||||||
|
@ -14,7 +14,8 @@ proc parseGraphUser*(json: string): User =
|
||||||
|
|
||||||
result = raw.data.userResult.result.legacy
|
result = raw.data.userResult.result.legacy
|
||||||
result.id = raw.data.userResult.result.restId
|
result.id = raw.data.userResult.result.restId
|
||||||
result.verified = result.verified or raw.data.userResult.result.isBlueVerified
|
if result.verifiedType == none and raw.data.userResult.result.isBlueVerified:
|
||||||
|
result.verifiedType = blue
|
||||||
|
|
||||||
proc parseGraphListMembers*(json, cursor: string): Result[User] =
|
proc parseGraphListMembers*(json, cursor: string): Result[User] =
|
||||||
result = Result[User](
|
result = Result[User](
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import std/[options, tables, strutils, strformat, sugar]
|
import std/[options, tables, strutils, strformat, sugar]
|
||||||
import jsony
|
import jsony
|
||||||
import user
|
import user, ../types/unifiedcard
|
||||||
import ../types/unifiedcard
|
|
||||||
from ../../types import Card, CardKind, Video
|
from ../../types import Card, CardKind, Video
|
||||||
from ../../utils import twimg, https
|
from ../../utils import twimg, https
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ proc toUser*(raw: RawUser): User =
|
||||||
tweets: raw.statusesCount,
|
tweets: raw.statusesCount,
|
||||||
likes: raw.favouritesCount,
|
likes: raw.favouritesCount,
|
||||||
media: raw.mediaCount,
|
media: raw.mediaCount,
|
||||||
verified: raw.verified or raw.extIsBlueVerified,
|
verifiedType: raw.verifiedType,
|
||||||
protected: raw.protected,
|
protected: raw.protected,
|
||||||
joinDate: parseTwitterDate(raw.createdAt),
|
joinDate: parseTwitterDate(raw.createdAt),
|
||||||
banner: getBanner(raw),
|
banner: getBanner(raw),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import options
|
import options
|
||||||
import common
|
import common
|
||||||
|
from ../../types import VerifiedType
|
||||||
|
|
||||||
type
|
type
|
||||||
RawUser* = object
|
RawUser* = object
|
||||||
|
@ -15,8 +16,7 @@ type
|
||||||
favouritesCount*: int
|
favouritesCount*: int
|
||||||
statusesCount*: int
|
statusesCount*: int
|
||||||
mediaCount*: int
|
mediaCount*: int
|
||||||
verified*: bool
|
verifiedType*: VerifiedType
|
||||||
extIsBlueVerified*: bool
|
|
||||||
protected*: bool
|
protected*: bool
|
||||||
profileLinkColor*: string
|
profileLinkColor*: string
|
||||||
profileBannerUrl*: string
|
profileBannerUrl*: string
|
||||||
|
|
|
@ -21,7 +21,7 @@ proc parseUser(js: JsonNode; id=""): User =
|
||||||
tweets: js{"statuses_count"}.getInt,
|
tweets: js{"statuses_count"}.getInt,
|
||||||
likes: js{"favourites_count"}.getInt,
|
likes: js{"favourites_count"}.getInt,
|
||||||
media: js{"media_count"}.getInt,
|
media: js{"media_count"}.getInt,
|
||||||
verified: js{"verified"}.getBool or js{"ext_is_blue_verified"}.getBool,
|
verifiedType: parseEnum[VerifiedType](js{"verified_type"}.getStr("None")),
|
||||||
protected: js{"protected"}.getBool,
|
protected: js{"protected"}.getBool,
|
||||||
joinDate: js{"created_at"}.getTime
|
joinDate: js{"created_at"}.getTime
|
||||||
)
|
)
|
||||||
|
@ -34,8 +34,8 @@ proc parseGraphUser(js: JsonNode): User =
|
||||||
user = ? js{"user_results", "result"}
|
user = ? js{"user_results", "result"}
|
||||||
result = parseUser(user{"legacy"})
|
result = parseUser(user{"legacy"})
|
||||||
|
|
||||||
if "is_blue_verified" in user:
|
if result.verifiedType == none and user{"is_blue_verified"}.getBool(false):
|
||||||
result.verified = user{"is_blue_verified"}.getBool()
|
result.verifiedType = blue
|
||||||
|
|
||||||
proc parseGraphList*(js: JsonNode): List =
|
proc parseGraphList*(js: JsonNode): List =
|
||||||
if js.isNull: return
|
if js.isNull: return
|
||||||
|
|
|
@ -52,6 +52,7 @@ proc initRedisPool*(cfg: Config) {.async.} =
|
||||||
await migrate("profileDates", "p:*")
|
await migrate("profileDates", "p:*")
|
||||||
await migrate("profileStats", "p:*")
|
await migrate("profileStats", "p:*")
|
||||||
await migrate("userType", "p:*")
|
await migrate("userType", "p:*")
|
||||||
|
await migrate("verifiedType", "p:*")
|
||||||
|
|
||||||
pool.withAcquire(r):
|
pool.withAcquire(r):
|
||||||
# optimize memory usage for user ID buckets
|
# optimize memory usage for user ID buckets
|
||||||
|
|
|
@ -28,6 +28,8 @@ $more_replies_dots: #AD433B;
|
||||||
$error_red: #420A05;
|
$error_red: #420A05;
|
||||||
|
|
||||||
$verified_blue: #1DA1F2;
|
$verified_blue: #1DA1F2;
|
||||||
|
$verified_business: #FAC82B;
|
||||||
|
$verified_government: #C1B6A4;
|
||||||
$icon_text: $fg_color;
|
$icon_text: $fg_color;
|
||||||
|
|
||||||
$tab: $fg_color;
|
$tab: $fg_color;
|
||||||
|
|
|
@ -39,6 +39,8 @@ body {
|
||||||
--error_red: #{$error_red};
|
--error_red: #{$error_red};
|
||||||
|
|
||||||
--verified_blue: #{$verified_blue};
|
--verified_blue: #{$verified_blue};
|
||||||
|
--verified_business: #{$verified_business};
|
||||||
|
--verified_government: #{$verified_government};
|
||||||
--icon_text: #{$icon_text};
|
--icon_text: #{$icon_text};
|
||||||
|
|
||||||
--tab: #{$fg_color};
|
--tab: #{$fg_color};
|
||||||
|
@ -141,17 +143,30 @@ ul {
|
||||||
|
|
||||||
.verified-icon {
|
.verified-icon {
|
||||||
color: var(--icon_text);
|
color: var(--icon_text);
|
||||||
background-color: var(--verified_blue);
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 2px 0 3px 3px;
|
margin: 2px 0 3px 3px;
|
||||||
padding-top: 2px;
|
padding-top: 3px;
|
||||||
height: 12px;
|
height: 11px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
background-color: var(--verified_blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.business {
|
||||||
|
color: var(--bg_panel);
|
||||||
|
background-color: var(--verified_business);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.government {
|
||||||
|
color: var(--bg_panel);
|
||||||
|
background-color: var(--verified_government);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: 600px) {
|
@media(max-width: 600px) {
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
button {
|
button {
|
||||||
margin: 0 2px 0 0;
|
margin: 0 2px 0 0;
|
||||||
height: 23px;
|
height: 23px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pref-input {
|
.pref-input {
|
||||||
|
|
|
@ -10,9 +10,7 @@ type
|
||||||
BadClientError* = object of CatchableError
|
BadClientError* = object of CatchableError
|
||||||
|
|
||||||
TimelineKind* {.pure.} = enum
|
TimelineKind* {.pure.} = enum
|
||||||
tweets
|
tweets, replies, media
|
||||||
replies
|
|
||||||
media
|
|
||||||
|
|
||||||
Api* {.pure.} = enum
|
Api* {.pure.} = enum
|
||||||
tweetDetail
|
tweetDetail
|
||||||
|
@ -63,6 +61,12 @@ type
|
||||||
tweetUnavailable = 421
|
tweetUnavailable = 421
|
||||||
tweetCensored = 422
|
tweetCensored = 422
|
||||||
|
|
||||||
|
VerifiedType* = enum
|
||||||
|
none = "None"
|
||||||
|
blue = "Blue"
|
||||||
|
business = "Business"
|
||||||
|
government = "Government"
|
||||||
|
|
||||||
User* = object
|
User* = object
|
||||||
id*: string
|
id*: string
|
||||||
username*: string
|
username*: string
|
||||||
|
@ -78,7 +82,7 @@ type
|
||||||
tweets*: int
|
tweets*: int
|
||||||
likes*: int
|
likes*: int
|
||||||
media*: int
|
media*: int
|
||||||
verified*: bool
|
verifiedType*: VerifiedType
|
||||||
protected*: bool
|
protected*: bool
|
||||||
suspended*: bool
|
suspended*: bool
|
||||||
joinDate*: DateTime
|
joinDate*: DateTime
|
||||||
|
|
|
@ -52,7 +52,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
||||||
let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
|
let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
|
||||||
|
|
||||||
buildHtml(head):
|
buildHtml(head):
|
||||||
link(rel="stylesheet", type="text/css", href="/css/style.css?v=18")
|
link(rel="stylesheet", type="text/css", href="/css/style.css?v=19")
|
||||||
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2")
|
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2")
|
||||||
|
|
||||||
if theme.len > 0:
|
if theme.len > 0:
|
||||||
|
|
|
@ -23,6 +23,13 @@ proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode =
|
||||||
if text.len > 0:
|
if text.len > 0:
|
||||||
text " " & text
|
text " " & text
|
||||||
|
|
||||||
|
template verifiedIcon*(user: User): untyped {.dirty.} =
|
||||||
|
if user.verifiedType != none:
|
||||||
|
let lower = ($user.verifiedType).toLowerAscii()
|
||||||
|
icon "ok", class=(&"verified-icon {lower}"), title=(&"Verified {lower} account")
|
||||||
|
else:
|
||||||
|
text ""
|
||||||
|
|
||||||
proc linkUser*(user: User, class=""): VNode =
|
proc linkUser*(user: User, class=""): VNode =
|
||||||
let
|
let
|
||||||
isName = "username" notin class
|
isName = "username" notin class
|
||||||
|
@ -32,11 +39,11 @@ proc linkUser*(user: User, class=""): VNode =
|
||||||
|
|
||||||
buildHtml(a(href=href, class=class, title=nameText)):
|
buildHtml(a(href=href, class=class, title=nameText)):
|
||||||
text nameText
|
text nameText
|
||||||
if isName and user.verified:
|
if isName:
|
||||||
icon "ok", class="verified-icon", title="Verified account"
|
verifiedIcon(user)
|
||||||
if isName and user.protected:
|
if user.protected:
|
||||||
text " "
|
text " "
|
||||||
icon "lock", title="Protected account"
|
icon "lock", title="Protected account"
|
||||||
|
|
||||||
proc linkText*(text: string; class=""): VNode =
|
proc linkText*(text: string; class=""): VNode =
|
||||||
let url = if "http" notin text: https & text else: text
|
let url = if "http" notin text: https & text else: text
|
||||||
|
|
|
@ -200,8 +200,7 @@ proc renderAttribution(user: User; prefs: Prefs): VNode =
|
||||||
buildHtml(a(class="attribution", href=("/" & user.username))):
|
buildHtml(a(class="attribution", href=("/" & user.username))):
|
||||||
renderMiniAvatar(user, prefs)
|
renderMiniAvatar(user, prefs)
|
||||||
strong: text user.fullname
|
strong: text user.fullname
|
||||||
if user.verified:
|
verifiedIcon(user)
|
||||||
icon "ok", class="verified-icon", title="Verified account"
|
|
||||||
|
|
||||||
proc renderMediaTags(tags: seq[User]): VNode =
|
proc renderMediaTags(tags: seq[User]): VNode =
|
||||||
buildHtml(tdiv(class="media-tag-block")):
|
buildHtml(tdiv(class="media-tag-block")):
|
||||||
|
|
Loading…
Reference in New Issue