Merge remote-tracking branch 'origin/master' into release/next-sys

This commit is contained in:
Philip Monk 2020-07-30 13:46:13 -07:00
commit 4a5d087de6
No known key found for this signature in database
GPG Key ID: B66E1F02604E44EC
44 changed files with 832 additions and 303 deletions

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:82fbb8acc02fbd0c3ffd8604ff5c1f3e96061abba47041decd9d0220f101cc3e
size 6293061
oid sha256:d862132e99ed393d786fb4bdb7c487bced2d4c9efc2cb7e174a73b6acca4799d
size 6304536

View File

@ -72,7 +72,7 @@
+$ glyph char
++ glyphs "!@#$%^&()-=_+[]\{}'\\:\",.<>?"
::
+$ nu-security ?(%channel %village %village-with-group)
+$ nu-security ?(%channel %village)
::
+$ command
$% [%target (set target)] :: set messaging target
@ -81,7 +81,7 @@
::
::
:: create chat
[%create nu-security path (unit glyph) (unit ?)]
[%create nu-security path (unit resource) (unit glyph) (unit ?)]
[%delete path] :: delete chat
[%invite [? path] (set ship)] :: allow
[%banish [? path] (set ship)] :: disallow
@ -293,8 +293,6 @@
::
++ target-to-path
|= target
%+ weld
?:(in-group ~ /~)
[(scot %p ship) path]
:: +path-to-target: deduces a target from a mailbox path
::
@ -464,6 +462,7 @@
security
;~ plug
path
(punt ;~(pfix ace group))
(punt ;~(pfix ace glyph))
(punt ;~(pfix ace (fuss 'y' 'n')))
==
@ -535,16 +534,15 @@
:: ;~(pfix ace ;~(plug i.opt $(opt t.opt)))
:: --
::
++ group ;~((glue net) ship sym)
++ tag |*(a=@tas (cold a (jest a))) ::TODO into stdlib
++ ship ;~(pfix sig fed:ag)
++ path ;~(pfix net ;~(plug urs:ab (easy ~))) ::NOTE short only, tmp
:: +mang: un/managed indicator prefix
::
++ mang
;~ pose
(cold %| (jest '~/'))
(cold %& (easy ~))
==
:: deprecated, as sig prefix is no longer used
::
++ mang (cold %& (easy ~))
:: +tarl: local target, as /path
::
++ tarl (stag our-self path)
@ -585,7 +583,7 @@
:: +security: security mode
::
++ security
(perk %channel %village-with-group %village ~)
(perk %channel %village ~)
::
:: +glyph: shorthand character
::
@ -741,15 +739,21 @@
:: +create: new local mailbox
::
++ create
|= [security=nu-security =path gyf=(unit char) allow-history=(unit ?)]
|= $: security=nu-security
=path
ugroup=(unit resource)
gyf=(unit char)
allow-history=(unit ?)
==
^- (quip card _state)
=/ with-group=? ?=(%village-with-group security)
=/ with-group=? ?=(^ ugroup)
=/ =target [with-group our-self path]
=/ real-path=^path (target-to-path target)
=/ group-path=^path ?~(ugroup ship+real-path (en-path:resource u.ugroup))
=/ =policy
?- security
%channel *open:policy
?(%village %village-with-group) *invite:policy
%channel *open:policy
%village *invite:policy
==
?^ (scry-for (unit mailbox:store) %chat-store [%mailbox real-path])
=- [[- ~] state]
@ -767,7 +771,7 @@
(rsh 3 1 (spat path))
''
real-path :: chat
real-path :: group
group-path :: group
policy
~
(fall allow-history %.y)

View File

@ -18,17 +18,17 @@
state-1
state-2
state-3
state-4
state-5
state-6
==
::
+$ state-3
$: %3
state-base
==
+$ state-6 [%6 state-base]
+$ state-5 [%5 state-base]
+$ state-4 [%4 state-base]
+$ state-3 [%3 state-base]
+$ state-2 [%2 state-base]
::
+$ state-2
$: %2
state-base
==
+$ state-1
$: %1
loaded-cards=*
@ -52,7 +52,7 @@
$% [%chat-update update:store]
==
--
=| state-3
=| state-6
=* state -
::
%- agent:dbug
@ -81,8 +81,14 @@
=/ old !<(versioned-state old-vase)
=| cards=(list card)
|-
?: ?=(%3 -.old)
?: ?=(%6 -.old)
[cards this(state old)]
?: ?=(?(%3 %4 %5) -.old)
=. cards
%+ weld cards
^- (list card)
[%pass /pokeme %agent [our.bol %chat-hook] %poke %noun !>(%fix-dm)]~
$(-.old %6)
?: ?=(%2 -.old)
=. cards
%+ weld cards
@ -319,9 +325,9 @@
^- (quip card _this)
=^ cards state
?+ mark (on-poke:def mark vase)
%json (poke-json:cc !<(json vase))
%chat-action (poke-chat-action:cc !<(action:store vase))
%noun [~ state]
%json (poke-json:cc !<(json vase))
%chat-action (poke-chat-action:cc !<(action:store vase))
%noun (poke-fix-dms:cc %fix-dms)
::
%chat-hook-action
(poke-chat-hook-action:cc !<(action:hook vase))
@ -383,6 +389,52 @@
|_ bol=bowl:gall
++ grp ~(. grpl bol)
::
++ poke-fix-dms
|= a=%fix-dms
^- (quip card _state)
:_ state
%- zing
%+ turn
~(tap by synced)
|= [=path host=ship]
^- (list card)
?> ?=([@ @ *] path)
=/ =ship (slav %p i.path)
?: =(ship our.bol)
:: local dm, no need to do cleanup
~
?: ?=(^ (groups-of-chat path))
:: correctly initialized, no need to do cleanup
::
~
?. =((end 3 4 i.t.path) 'dm--')
~
:- =- [%pass /fixdm %agent [our.bol %chat-view] %poke %chat-view-action -]
!> ^- action:view
[%delete path]
=/ new-dm /(scot %p our.bol)/(crip (weld "dm--" (trip (scot %p ship))))
=/ mailbox=(unit mailbox:store) (chat-scry path)
?~ mailbox
~
:~ =- [%pass /fixdm %agent [our.bol %chat-view] %poke %chat-view-action -]
!> ^- action:view
:* %create
%- crip
(zing [(trip (scot %p our.bol)) " <-> " (trip (scot %p ship)) ~])
''
new-dm
ship+new-dm
[%invite (silt ~[ship])]
(silt ~[ship])
%.y
%.n
==
::
=- [%pass /fixdm %agent [our.bol %chat-store] %poke %chat-action -]
!> ^- action:store
[%messages new-dm envelopes.u.mailbox]
==
::
++ poke-json
|= jon=json
^- (quip card _state)

View File

@ -269,7 +269,7 @@
==
::
++ on-leave on-leave:def
++ on-peek
++ on-peek
|= =path
^- (unit (unit cage))
|^
@ -287,10 +287,9 @@
*@uv
=/ parent (scot %p ship.u.ota)
=+ .^(=cass:clay %cs /[parent]/[desk.u.ota]/1/late/foo)
%^ end 3 3
%^ end 0 25
.^(@uv %cz /[parent]/[desk.u.ota]/(scot %ud ud.cass))
--
++ on-agent on-agent:def
++ on-fail on-fail:def
--

View File

@ -1,7 +1,7 @@
/- glob
/+ default-agent, verb, dbug
|%
++ hash 0v5.knd3c.vvtvt.h0gg0.8qcau.8iii4
++ hash 0v5.m40lm.ha96c.aavqb.57scm.222e7
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
+$ all-states
$% state-0

View File

@ -85,15 +85,16 @@
old [%2 +.old]
::
cards
%+ turn
%+ murn
~(tap in ~(key by group-indices.old))
|= =group-path
^- card
=/ rid=resource
(de-path:resource group-path)
?: =(our.bowl entity.rid)
(poke-md-hook %add-owned group-path)
(poke-md-hook %add-synced entity.rid group-path)
^- (unit card)
=/ rid=(unit resource)
(de-path-soft:resource group-path)
?~ rid ~
?: =(our.bowl entity.u.rid)
`(poke-md-hook %add-owned group-path)
`(poke-md-hook %add-synced entity.u.rid group-path)
==
=/ new-state=state-one
%* . *state-one
@ -254,6 +255,11 @@
=/ =group-path (stab (slav %t i.t.t.path))
=/ =md-resource [`@tas`i.t.t.t.path (stab (slav %t i.t.t.t.t.path))]
``noun+!>((~(get by associations) [group-path md-resource]))
::
[%x %resource @ *]
=/ app=@tas i.t.t.path
=/ app-path=^path t.t.t.path
``noun+!>((~(get by resource-indices) app app-path))
==
::
++ on-agent on-agent:def

View File

@ -54,6 +54,7 @@
[%3 state-three]
[%4 state-three]
[%5 state-three]
[%6 state-three]
==
::
+$ metadata-delta
@ -69,7 +70,7 @@
==
--
::
=| [%5 state-three]
=| [%6 state-three]
=* state -
%- agent:dbug
%+ verb |
@ -86,7 +87,6 @@
:_ this
:~ [%pass /view-bind %arvo %e %connect [~ /'publish-view'] %publish]
[%pass /read/paths %arvo %c %warp our.bol q.byk.bol `rav]
[%pass /permissions %agent [our.bol %permission-store] %watch /updates]
(invite-poke:main [%create /publish])
:* %pass /invites %agent [our.bol %invite-store] %watch
/invitatory/publish
@ -218,6 +218,26 @@
==
::
%5
%= $
-.p.old-state %6
cards
%+ weld cards
%+ roll ~(tap by books.p.old-state)
|= [[[who=@p book=@tas] nb=notebook] out=(list card)]
^- (list card)
?. =(who our.bol)
out
=/ rid (de-path:resource writers.nb)
=/ grp=(unit group) (scry-group:grup:main rid)
?~ grp out
?: hidden.u.grp
out
=/ =tag [%publish (cat 3 'writers-' book)]
:_ out
(group-proxy-poke entity.rid %add-tag rid tag members.u.grp)
==
::
%6
[cards this(state p.old-state)]
==
++ convert-notebook-3-4
@ -995,6 +1015,22 @@
[~ state]
:_ state
%- zing
:- ^- (list card)
%+ roll ~(tap by books)
|= [[[who=@p book=@tas] nb=notebook] out=(list card)]
^- (list card)
?. =(who our.bol)
out
?. =(writers.nb path)
out
=/ rid (de-path:resource writers.nb)
=/ grp=(unit group) (scry-group:grup rid)
?~ grp out
?: hidden.u.grp
out
=/ =tag [%publish (cat 3 'writers-' book)]
:_ out
(group-proxy-poke entity.rid %add-tag rid tag members.u.grp)
%+ turn ~(tap in ships)
|= who=@p
?. (allowed who %read u.book)
@ -1226,12 +1262,19 @@
^- [(list card) write=path read=path]
?> ?=(^ group-path.group)
=/ scry-path
;:(welp /(scot %p our.bol)/group-store/(scot %da now.bol) [%groups group-path.group] /noun)
=/ grp .^((unit ^group) %gx scry-path)
;: welp
/(scot %p our.bol)/group-store/(scot %da now.bol)
[%groups group-path.group]
/noun
==
=/ rid=resource (de-path:resource group-path.group)
=/ grp=(unit ^group) (scry-group:grup rid)
?: use-preexisting.group
?~ grp !!
?. (is-managed group-path.group) !!
`[group-path.group group-path.group]
=/ =tag [%publish (cat 3 'writers-' book)]
:_ [group-path.group group-path.group]
[(group-proxy-poke entity.rid %add-tag rid tag members.u.grp)]~
::
=/ =policy
*open:policy
@ -1684,10 +1727,9 @@
?> ?=(^ subscribers.u.book)
=/ cards=(list card)
~[(delete-dir pax)]
=/ rid=resource
(de-path:resource writers.u.book)
=? cards (is-managed:grup rid)
=? cards !(is-managed:grup rid)
[(group-poke %remove-group rid ~) cards]
[cards state]
:: %del-note:
@ -1791,6 +1833,10 @@
?> (team:title our.bol src.bol)
=/ join-wire=wire
/join-group/[(scot %p who.act)]/[book.act]
=/ meta=(unit (set path))
(metadata-resource-scry %publish /(scot %p who.act)/[book.act])
?^ meta
(subscribe-notebook who.act book.act)
=/ rid=resource
[who.act book.act]
=/ =cage
@ -1811,12 +1857,16 @@
(de-path:resource writers.book)
=/ =group
(need (scry-group:grup rid))
:_ state(books (~(del by books) who.act book.act))
:~ `card`[%pass wir %agent [who.act %publish] %leave ~]
`card`[%give %fact [/primary]~ %publish-primary-delta !>(del)]
(group-proxy-poke who.act %remove-members rid (sy our.bol ~))
(group-poke %remove-group rid ~)
==
=/ cards=(list card)
:~ [%pass wir %agent [who.act %publish] %leave ~]
[%give %fact [/primary]~ %publish-primary-delta !>(del)]
==
=? cards hidden.group
%+ weld cards
:~ (group-proxy-poke who.act %remove-members rid (sy our.bol ~))
(group-poke %remove-group rid ~)
==
[cards state(books (~(del by books) who.act book.act))]
:: %read
::
%read
@ -1952,6 +2002,19 @@
/noun
==
::
++ metadata-resource-scry
|= [app=@tas app-path=path]
^- (unit (set path))
?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) ~
.^ (unit (set path))
%gx
;: weld
/(scot %p our.bol)/metadata-store/(scot %da now.bol)/resource/[app]
app-path
/noun
==
==
::
++ emit-metadata
|= del=metadata-delta
^- (list card)
@ -2044,9 +2107,11 @@
(emit-updates-and-state host.del book.del data.del del sty)
=/ rid=resource
(de-path:resource writers.data.del)
=? cards !=(our.bol entity.rid)
:_ cards
(group-pull-hook-poke [%add host.del rid])
:_ state
:* (group-pull-hook-poke [%add host.del rid])
(metadata-hook-poke [%add-synced host.del writers.data.del])
:* (metadata-hook-poke [%add-synced host.del writers.data.del])
cards
==
::

View File

@ -36,7 +36,7 @@
public-key
=/ cub (pit:nu:crub:crypto 512 (shaz (jam mon life eny)))
=/ =seed:able:jael
[mon 1 sec:ex:cub ~]
[mon life sec:ex:cub ~]
%- %- slog
:~ leaf+"moon: {(scow %p mon)}"
leaf+(scow %uw (jam seed))

View File

@ -8,7 +8,11 @@
::
:- %say
|= $: [now=@da eny=@uvJ bec=beak]
[arg=?(~ [her=@p sud=@tas ~]) ~]
[arg=?(~ [%disable ~] [her=@p sud=@tas ~]) ~]
==
?~ arg
:- %kiln-ota-info ~
:- %kiln-ota
?~(arg ~ `[her sud]:arg)
?: ?=([%disable ~] arg)
~
`[her sud]:arg

View File

@ -5,5 +5,5 @@
[%tang >timers< ~]
.^ (list [date=@da =duct])
%bx
(en-beam:format [p.bec %$ r.bec] /debug/timers)
(en-beam:format [p.bec %$ r.bec] /timers/debug)
==

View File

@ -341,13 +341,22 @@
abet:(spam (render "already syncing" [sud her syd]:hos) ~)
abet:abet:start-sync:(auto hos)
::
++ ota-info
?~ ota
"OTAs disabled"
"OTAs enabled from {<desk.u.ota>} on {<ship.u.ota>}"
::
++ poke-ota-info
|= *
=< abet %- spam
:~ [%leaf ota-info]
[%leaf "use |ota %disable or |ota ~sponsor %kids to reset it"]
==
::
++ poke-syncs :: print sync config
|= ~
=< abet %- spam
:- :- %leaf
?~ ota
"OTAs disabled"
"OTAs from {<desk.u.ota>} on {<ship.u.ota>}"
:- [%leaf ota-info]
?: =(0 ~(wyt by syn))
[%leaf "no other syncs configured"]~
%+ turn ~(tap in ~(key by syn))
@ -416,6 +425,7 @@
%kiln-merge =;(f (f !<(_+<.f vase)) poke-merge)
%kiln-mount =;(f (f !<(_+<.f vase)) poke-mount)
%kiln-ota =;(f (f !<(_+<.f vase)) poke:update)
%kiln-ota-info =;(f (f !<(_+<.f vase)) poke-ota-info)
%kiln-permission =;(f (f !<(_+<.f vase)) poke-permission)
%kiln-rm =;(f (f !<(_+<.f vase)) poke-rm)
%kiln-schedule =;(f (f !<(_+<.f vase)) poke-schedule)

Binary file not shown.

View File

@ -5,13 +5,17 @@
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.10.5",
"@reach/disclosure": "^0.10.5",
"@reach/menu-button": "^0.10.1",
"@tlon/indigo-react": "^1.1.10",
"@reach/tabs": "^0.10.5",
"@tlon/indigo-light": "^1.0.3",
"@tlon/indigo-react": "^1.1.15",
"classnames": "^2.2.6",
"codemirror": "^5.51.0",
"css-loader": "^3.5.3",
"formik": "^2.1.4",
"lodash": "^4.17.15",
"markdown-to-jsx": "^6.11.4",
"moment": "^2.20.1",
"mousetrap": "^1.6.5",
"prop-types": "^15.7.2",

View File

@ -6,7 +6,8 @@ import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
import './css/indigo-static.css';
import './css/fonts.css';
import { light, dark, inverted, paperDark } from '@tlon/indigo-react';
import light from './themes/light';
import dark from './themes/old-dark';
import LaunchApp from './apps/launch/app';
import ChatApp from './apps/chat/app';
@ -16,7 +17,7 @@ import LinksApp from './apps/links/app';
import PublishApp from './apps/publish/app';
import StatusBar from './components/StatusBar';
import NotFound from './components/404';
import ErrorComponent from './components/Error';
import GlobalStore from './store/store';
import GlobalSubscription from './subscription/global';
@ -85,7 +86,7 @@ class App extends React.Component {
const associations = this.state.associations ? this.state.associations : { contacts: {} };
const selectedGroups = this.state.selectedGroups ? this.state.selectedGroups : [];
const { state } = this;
const theme = state.dark ? paperDark : light;
const theme = state.dark ? dark : light;
return (
<ThemeProvider theme={theme}>
@ -161,7 +162,11 @@ class App extends React.Component {
/>
)}
/>
<Route component={NotFound} />
<Route
render={(props) => (
<ErrorComponent {...props} code={404} description="Not Found" />
)}
/>
</Switch>
</Content>
</Router>

View File

@ -94,6 +94,7 @@ interface ChatScreenState {
scrollLocked: boolean;
read: number;
active: boolean;
messages: Map<string, string>;
lastScrollHeight: number | null;
}
@ -118,6 +119,7 @@ export class ChatScreen extends Component<ChatScreenProps, ChatScreenState> {
scrollLocked: false,
read: props.read,
active: true,
messages: new Map(),
// only for FF
lastScrollHeight: null,
};
@ -594,8 +596,12 @@ export class ChatScreen extends Component<ChatScreenProps, ChatScreenState> {
envelopes={props.envelopes}
contacts={props.contacts}
onEnter={() => this.setState({ scrollLocked: false })}
onUnmount={(msg: string) => this.setState({
messages: this.state.messages.set(props.station, msg)
})}
s3={props.s3}
placeholder="Message..."
message={this.state.messages.get(props.station) || ""}
/>
</div>
);

View File

@ -40,7 +40,7 @@ export class ChatInput extends Component {
super(props);
this.state = {
message: '',
message: props.message,
patpSearch: null
};
@ -99,6 +99,10 @@ export class ChatInput extends Component {
});
}
componentWillUnmount() {
this.props.onUnmount(this.state.message);
}
nextAutocompleteSuggestion(backward = false) {
const { patpSuggestions } = this.state;
let idx = patpSuggestions.findIndex(s => s === this.state.selectedSuggestion);
@ -150,6 +154,9 @@ export class ChatInput extends Component {
if(patpSearch !== null) {
this.patpAutocomplete(value, false);
}
this.setState({
message: value
});
}
getLetterType(letter) {
@ -364,6 +371,7 @@ export class ChatInput extends Component {
style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 72px)' }}
>
<CodeEditor
value={this.props.message}
options={options}
editorDidMount={(editor) => {
this.editor = editor;

View File

@ -6,6 +6,7 @@ import { Spinner } from '../../../components/Spinner';
import { ChatTabBar } from './lib/chat-tabbar';
import { InviteSearch } from '../../../components/InviteSearch';
import SidebarSwitcher from '../../../components/SidebarSwitch';
import Toggle from '../../../components/toggle';
export class SettingsScreen extends Component {
constructor(props) {
@ -195,18 +196,12 @@ export class SettingsScreen extends Component {
} else {
let inclusiveToggle = <div />;
if (state.targetGroup) {
// TODO toggle component into /lib
const inclusiveClasses = state.inclusive
? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
: 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
inclusiveToggle = (
<div className="mt4">
<input
type="checkbox"
style={{ WebkitAppearance: 'none', width: 28 }}
className={inclusiveClasses}
onChange={this.changeInclusive}
/>
<Toggle
boolean={state.inclusive}
change={this.changeInclusive}
/>
<span className="dib f9 white-d inter ml3">
Add all members to group
</span>

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import Welcome from './lib/welcome';
import { alphabetiseAssociations } from '../../../lib/util';
import { SidebarInvite } from './lib/sidebar-invite';
import SidebarInvite from '../../../components/SidebarInvite';
import { GroupItem } from './lib/group-item';
export class Sidebar extends Component {
@ -51,10 +51,10 @@ export class Sidebar extends Component {
.map((uid) => {
return (
<SidebarInvite
uid={uid}
key={uid}
invite={props.invites[uid]}
api={props.api}
onAccept={() => props.api.invite.accept('/chat', uid)}
onDecline={() => props.api.invite.decline('/chat', uid)}
/>
);
});

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
import ErrorBoundary from '../../../components/ErrorBoundary';
export class Skeleton extends Component {
render() {
@ -61,7 +62,9 @@ export class Skeleton extends Component {
width: 'calc(100% - 300px)'
}}
>
{this.props.children}
<ErrorBoundary>
{this.props.children}
</ErrorBoundary>
</div>
</div>
</div>

View File

@ -6,7 +6,6 @@ import urbitOb from 'urbit-ob';
export class JoinScreen extends Component {
constructor(props) {
super(props);
this.state = {
group: '',
error: false,
@ -21,12 +20,11 @@ export class JoinScreen extends Component {
this.componentDidUpdate();
}
componentDidUpdate(prevProps) {
componentDidUpdate() {
const { props, state } = this;
// autojoin by URL, waits for group information
if ((props.ship && props.name) &&
(prevProps && (prevProps.groups !== props.groups))) {
console.log('autojoining');
(props.contacts && (Object.keys(props.contacts).length > 0) && !state.group)) {
const incomingGroup = `${props.ship}/${props.name}`;
// push to group if already exists
if (`/ship/${incomingGroup}` in props.groups) {
@ -48,10 +46,8 @@ export class JoinScreen extends Component {
}
}
onClickJoin() {
const { props, state } = this;
console.log('i am joining');
const { group } = state;
const [ship, name] = group.split('/');
@ -101,14 +97,12 @@ export class JoinScreen extends Component {
<p className="f8 lh-copy mt3 db">Enter a <span className="mono">~ship/group-name</span></p>
<p className="f9 gray2 mb4">Group names use lowercase, hyphens, and slashes.</p>
<textarea
ref={ (e) => {
this.textarea = e;
} }
className={'f7 mono ba bg-gray0-d white-d pa3 mb2 db ' +
'focus-b--black focus-b--white-d b--gray3 b--gray2-d nowrap '}
'focus-b--black focus-b--white-d b--gray3 b--gray2-d nowrap overflow-y-hidden'}
placeholder="~zod/group-name"
spellCheck="false"
rows={1}
cols={32}
onKeyPress={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
@ -116,7 +110,7 @@ export class JoinScreen extends Component {
}
}}
style={{
resize: 'none'
resize: 'none',
}}
onChange={this.groupChange}
value={this.state.group}

View File

@ -155,7 +155,7 @@ export class ContactSidebar extends Component<ContactSidebarProps, ContactSideba
<div className="overflow-auto h-100">
<Link
to={'/~groups/add' + props.path}
className={((props.path.includes(window.ship))
className={((role === "admin" || role === "moderator")
? 'dib'
: 'dn')}
>

View File

@ -1,8 +1,10 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Spinner } from '../../../../components/Spinner';
import { Toggle } from '../../../../components/toggle';
import { GroupView } from '../../../../components/Group';
import { deSig, uxToHex, writeText } from '../../../../lib/util';
import { roleForShip, resourceFromPath } from '../../../../lib/group';
export class GroupDetail extends Component {
constructor(props) {
@ -15,6 +17,7 @@ export class GroupDetail extends Component {
};
this.changeTitle = this.changeTitle.bind(this);
this.changeDescription = this.changeDescription.bind(this);
this.changePolicy = this.changePolicy.bind(this);
}
componentDidMount() {
@ -47,6 +50,17 @@ export class GroupDetail extends Component {
this.setState({ description: event.target.value });
}
changePolicy() {
this.setState({ awaiting: true }, () => {
this.props.api.groups.changePolicy(resourceFromPath(this.props.path),
Boolean(this.props.group?.policy?.open)
? { replace: { invite: { pending: [] } } }
: { replace: { open: { banned: [], banRanks: [] } } }
).then(() => this.setState({ awaiting: false }));
}
);
}
renderDetail() {
const { props } = this;
@ -175,7 +189,8 @@ export class GroupDetail extends Component {
const { group, association } = props;
const groupOwner = (deSig(props.match.params.ship) === window.ship);
const ourRole = roleForShip(group, window.ship);
const groupOwner = (ourRole === 'admin');
const deleteButtonClasses = (groupOwner) ? 'b--red2 red2 pointer bg-gray0-d' : 'b--gray3 gray3 bg-gray0-d c-default';
@ -280,7 +295,17 @@ export class GroupDetail extends Component {
}}
/>
</div>
<p className="f9 mt3 lh-copy">Delete Group</p>
<div className="relative w-100 mt6" style={{ maxWidth: '29rem' }}>
<Toggle
boolean={(Boolean(group?.policy?.invite))}
change={this.changePolicy}
/>
<span className="dib f9 white-d inter ml3">Private Group</span>
<p className="f9 gray2 pt1" style={{ paddingLeft: 40 }}>
If private, members must be invited
</p>
</div>
<p className="f9 mt6 lh-copy">Delete Group</p>
<p className="f9 gray2 mb2">
Permanently delete this group. All current members will no longer see this group.
</p>

View File

@ -3,7 +3,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { GroupItem } from './group-item';
import { Sigil } from '../../../../lib/sigil';
import { SidebarInvite } from './sidebar-invite';
import SidebarInvite from '../../../../components/SidebarInvite';
import { Welcome } from './welcome';
import { cite } from '../../../../lib/util';
@ -48,10 +48,18 @@ export class GroupSidebar extends Component {
return (
<SidebarInvite
key={uid}
api={props.api}
invite={invite}
uid={uid}
history={props.history}
onAccept={() => {
const [,,ship, name] = invite.path.split('/');
const resource = { ship, name };
api.contacts.join(resource).then(() => {
api.invite.accept('/contacts', uid);
});
props.history.push(`/~groups${invite.path}`);
}}
onDecline={() => {
api.invite.decline('/contacts', uid);
}}
/>
);
});

View File

@ -1,34 +0,0 @@
import React, { Component } from 'react';
export class SidebarInvite extends Component {
onAccept() {
const { props } = this;
const [,,ship, name] = props.invite.path.split('/');
const resource = { ship, name };
props.api.contacts.join(resource).then(() => {
props.api.invite.accept('/contacts', props.uid);
});
props.history.push(`/~groups${props.invite.path}`);
}
onDecline() {
this.props.api.invite.decline('/contacts', this.props.uid);
}
render() {
const { props } = this;
return (
<div className='pa3'>
<div className='w-100 v-mid'>
<p className="dib f8 mono white-d">
You have been invited to join {props.invite.path}
</p>
</div>
<a className="dib pointer pa2 f9 bg-green2 white mt4" onClick={this.onAccept.bind(this)}>Accept Invite</a>
<a className="dib pointer ml4 pa2 f9 bg-black white mt4" onClick={this.onDecline.bind(this)}>Decline</a>
</div>
);
}
}

View File

@ -3,6 +3,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { InviteSearch, Invites } from '../../../components/InviteSearch';
import { Spinner } from '../../../components/Spinner';
import { Toggle } from '../../../components/toggle';
import { RouteComponentProps } from 'react-router-dom';
import { Groups, GroupPolicy, Resource } from '../../../types/group-update';
import { Contacts, Rolodex } from '../../../types/contact-update';
@ -129,9 +130,6 @@ export class NewScreen extends Component<NewScreenProps, NewScreenState> {
</span>
);
}
const privacySwitchClasses = this.state.privacy
? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
: 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
return (
<div className='h-100 w-100 mw6 pa3 pt4 overflow-x-hidden bg-gray0-d white-d flex flex-column'>
@ -174,11 +172,9 @@ export class NewScreen extends Component<NewScreenProps, NewScreenState> {
onChange={this.descriptionChange}
/>
<div className='mv7'>
<input
type='checkbox'
style={{ WebkitAppearance: 'none', width: 28 }}
onChange={this.groupPrivacyChange}
className={privacySwitchClasses}
<Toggle
boolean={this.state.privacy}
change={this.groupPrivacyChange}
/>
<span className='dib f9 white-d inter ml3'>Private Group</span>
<p className='f9 gray2 pt1' style={{ paddingLeft: 40 }}>

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { GroupSidebar } from './lib/group-sidebar';
import ErrorBoundary from '../../../components/ErrorBoundary';
export class Skeleton extends Component {
render() {
@ -25,7 +26,9 @@ export class Skeleton extends Component {
className={'h-100 w-100 relative ' + rightPanelClasses}
style={{ flexGrow: 1 }}
>
{props.children}
<ErrorBoundary>
{props.children}
</ErrorBoundary>
</div>
</div>
</div>

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { GroupItem } from './group-item';
import { SidebarInvite } from './sidebar-invite';
import SidebarInvite from '../../../../components/SidebarInvite';
import { Welcome } from './welcome';
import { alphabetiseAssociations } from '../../../../lib/util';
@ -17,9 +17,9 @@ export class ChannelsSidebar extends Component {
return (
<SidebarInvite
key={uid}
uid={uid}
invite={props.invites[uid]}
api={props.api}
onAccept={() => props.api.invite.accept('/link', uid)}
onDecline={() => props.api.invite.decline('/link', uid)}
/>
);
});

View File

@ -1,37 +0,0 @@
import React, { Component } from 'react';
export class SidebarInvite extends Component {
onAccept() {
this.props.api.invite.accept('/link', this.props.uid);
}
onDecline() {
this.props.api.invite.decline('/link', this.props.uid);
}
render() {
const { props } = this;
return (
<div className='w-100 bg-transparent pa4 bb b--gray4 b--gray1-d'>
<div className='w-100 v-mid'>
<p className="dib f8 mono gray4-d">
{props.invite.text}
</p>
</div>
<a
className="dib pointer pa2 f9 bg-green2 white mt4"
onClick={this.onAccept.bind(this)}
>
Accept Invite
</a>
<a
className="dib pointer ml4 pa2 f9 bg-black bg-gray0-d white mt4"
onClick={this.onDecline.bind(this)}
>
Decline
</a>
</div>
);
}
}

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { ChannelsSidebar } from './lib/channel-sidebar';
import ErrorBoundary from '../../../components/ErrorBoundary';
export class Skeleton extends Component {
render() {
@ -40,7 +41,9 @@ export class Skeleton extends Component {
flexGrow: 1
}}
>
{this.props.children}
<ErrorBoundary>
{props.children}
</ErrorBoundary>
</div>
</div>
</div>

View File

@ -65,7 +65,7 @@ export default class PublishApp extends React.Component {
this.unreadTotal = unreadTotal;
}
const { api, groups, sidebarShown } = props;
const { api, groups, sidebarShown, invites } = props;
return (
<Switch>
@ -77,7 +77,7 @@ export default class PublishApp extends React.Component {
active={'sidebar'}
rightPanelHide={true}
sidebarShown={true}
invites={props.invites}
invites={invites}
notebooks={notebooks}
associations={associations}
selectedGroups={selectedGroups}
@ -108,7 +108,7 @@ export default class PublishApp extends React.Component {
active={'rightPanel'}
rightPanelHide={false}
sidebarShown={sidebarShown}
invites={props.invites}
invites={invites}
notebooks={notebooks}
associations={associations}
selectedGroups={selectedGroups}
@ -139,7 +139,7 @@ export default class PublishApp extends React.Component {
active={'rightPanel'}
rightPanelHide={false}
sidebarShown={sidebarShown}
invites={props.invites}
invites={invites}
notebooks={notebooks}
associations={associations}
selectedGroups={selectedGroups}
@ -185,7 +185,7 @@ export default class PublishApp extends React.Component {
active={'rightPanel'}
rightPanelHide={false}
sidebarShown={sidebarShown}
invites={props.invites}
invites={invites}
notebooks={notebooks}
associations={associations}
selectedGroups={selectedGroups}
@ -211,7 +211,7 @@ export default class PublishApp extends React.Component {
active={'rightPanel'}
rightPanelHide={false}
sidebarShown={sidebarShown}
invites={props.invites}
invites={invites}
notebooks={notebooks}
associations={associations}
contacts={contacts}
@ -263,7 +263,7 @@ export default class PublishApp extends React.Component {
active={'rightPanel'}
rightPanelHide={false}
sidebarShown={sidebarShown}
invites={props.invites}
invites={invites}
notebooks={notebooks}
selectedGroups={selectedGroups}
associations={associations}
@ -290,7 +290,7 @@ export default class PublishApp extends React.Component {
active={'rightPanel'}
rightPanelHide={false}
sidebarShown={sidebarShown}
invites={props.invites}
invites={invites}
notebooks={notebooks}
associations={associations}
selectedGroups={selectedGroups}

View File

@ -5,6 +5,7 @@ import { NotebookPosts } from './notebook-posts';
import { Subscribers } from './subscribers';
import { Settings } from './settings';
import { cite } from '../../../../lib/util';
import { roleForShip } from '../../../../lib/group';
export class Notebook extends Component {
constructor(props) {
@ -176,11 +177,15 @@ export class Notebook extends Component {
Unsubscribe
</button>;
const subsComponent = (this.props.ship.slice(1) !== window.ship)
? null
: <Link to={subs} className={tabStyles.subscribers}>
const group = props.groups[notebook?.['writers-group-path']];
const role = roleForShip(group, window.ship);
const subsComponent = (this.props.ship.slice(1) === window.ship) || (role === 'admin')
? (<Link to={subs} className={tabStyles.subscribers}>
Subscribers
</Link>;
</Link>)
: null
const settingsComponent = (this.props.ship.slice(1) !== window.ship)
? null

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { writeText } from '../../../../lib/util';
import { Spinner } from '../../../../components/Spinner';
import { InviteSearch } from '../../../../components/InviteSearch';
import Toggle from '../../../../components/toggle';
export class Settings extends Component {
constructor(props) {
@ -133,22 +133,16 @@ export class Settings extends Component {
// don't give the option to make inclusive if we don't own the target
// group
const targetOwned = (state.targetGroup)
? state.targetGroup.slice(0, window.ship.length+3) === `/~${window.ship}/`
? Boolean(state.targetGroup.includes(`/~${window.ship}/`))
: false;
let inclusiveToggle = <div />;
if (targetOwned) {
// TODO toggle component into /lib
const inclusiveClasses = state.inclusive
? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
: 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
inclusiveToggle = (
<div className="mt4">
<input
type="checkbox"
style={{ WebkitAppearance: 'none', width: 28 }}
className={inclusiveClasses}
onChange={this.changeInclusive}
/>
<Toggle
boolean={state.inclusive}
change={this.changeInclusive}
/>
<span className="dib f9 white-d inter ml3">
Add all members to group
</span>
@ -201,10 +195,6 @@ export class Settings extends Component {
}
render() {
const commentsSwitchClasses = (this.state.comments)
? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
: 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
if (this.props.host.slice(1) === window.ship) {
return (
<div className="flex-column">
@ -274,11 +264,9 @@ export class Settings extends Component {
/>
</div>
<div className="mv6">
<input
type="checkbox"
style={{ WebkitAppearance: 'none', width: 28 }}
className={commentsSwitchClasses}
onChange={this.changeComments}
<Toggle
boolean={this.state.comments}
change={this.changeComments}
/>
<span className="dib f9 white-d inter ml3">Comments</span>
<p className="f9 gray2 pt1" style={{ paddingLeft: 40 }}>
@ -293,7 +281,7 @@ export class Settings extends Component {
</div>
);
} else {
return copyShortcode;
return '';
}
}
}

View File

@ -1,38 +0,0 @@
import React, { Component } from 'react';
export class SidebarInvite extends Component {
onAccept() {
this.props.api.invite.accept('/publish', this.props.uid);
}
onDecline() {
this.props.api.invite.decline('/publish', this.props.uid);
}
render() {
const { props } = this;
return (
<div className='pa3 bb b--gray4 b--gray1-d'>
<div className='w-100 v-mid'>
<p className="dib f9 mono gray4-d">
{props.invite.text}
</p>
</div>
<a
className="dib pointer pa2 f9 bg-green2 white mt4"
onClick={this.onAccept.bind(this)}
>
Accept Invite
</a>
<a
className="dib pointer ml4 pa2 f9 bg-black bg-gray0-d white mt4"
onClick={this.onDecline.bind(this)}
>
Decline
</a>
</div>
);
}
}

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { SidebarInvite } from './sidebar-invite';
import SidebarInvite from '../../../../components/SidebarInvite';
import { Welcome } from './welcome';
import { GroupItem } from './group-item';
import { alphabetiseAssociations } from '../../../../lib/util';
@ -19,13 +19,13 @@ export class Sidebar extends Component {
const sidebarInvites = !(props.invites && props.invites['/publish'])
? null
: Object.keys(props.invites['/publish'])
.map((uid, i) => {
.map((uid) => {
return (
<SidebarInvite
uid={uid}
key={uid}
invite={props.invites['/publish'][uid]}
api={this.props.api}
key={i}
onAccept={() => props.api.invite.accept('/publish', uid)}
onDecline={() => props.api.invite.decline('/publish', uid)}
/>
);
});

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { GroupView } from '../../../../components/Group';
import { resourceFromPath } from '../../../../lib/group';
export class Subscribers extends Component {
constructor(props) {
@ -7,6 +8,7 @@ export class Subscribers extends Component {
this.redirect = this.redirect.bind(this);
this.addUser = this.addUser.bind(this);
this.removeUser = this.removeUser.bind(this);
this.addAll = this.addAll.bind(this);
}
addUser(who, path) {
@ -21,6 +23,18 @@ export class Subscribers extends Component {
window.location.href = url;
}
addAll() {
const path = this.props.notebook['writers-group-path'];
const group = path ? this.props.groups[path] : null;
const resource = resourceFromPath(path);
this.props.api.groups.addTag(
resource,
{ app: 'publish', tag: `writers-${this.props.book}` },
[...group.members].map(m => `~${m}`)
);
}
render() {
const path = this.props.notebook['writers-group-path'];
const group = path ? this.props.groups[path] : null;
@ -45,17 +59,25 @@ export class Subscribers extends Component {
];
return (
<GroupView
permissions
resourcePath={path}
group={group}
tags={tags}
appTags={appTags}
contacts={props.contacts}
groups={props.groups}
associations={props.associations}
api={this.props.api}
/>
<div>
<button
onClick={this.addAll}
className={'dib f9 black gray4-d bg-gray0-d ba pa2 mb4 b--black b--gray1-d pointer'}
>
Add all members as writers
</button>
<GroupView
permissions
resourcePath={path}
group={group}
tags={tags}
appTags={appTags}
contacts={this.props.contacts}
groups={this.props.groups}
associations={this.props.associations}
api={this.props.api}
/>
</div>
);
}
}

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { Sidebar } from './lib/sidebar';
import ErrorBoundary from '../../../components/ErrorBoundary';
export class Skeleton extends Component {
render() {
@ -36,7 +37,9 @@ export class Skeleton extends Component {
flexGrow: 1
}}
>
{props.children}
<ErrorBoundary>
{props.children}
</ErrorBoundary>
</div>
</div>
</div>

View File

@ -1,15 +0,0 @@
import React from 'react';
const NotFound = () => <div
className="fixed inter tc"
style={{
top: '50vh',
left: '50%',
transform: 'translate(-50%, -25vh)',
fontFeatureSettings: '\'zero\' 1' }}>
<h1 className="fw2 f2">404</h1>
<p className="tc">Not found.<br /><br />
If this is unexpected, email <code>support@tlon.io</code> or <a className="bb" href="https://github.com/urbit/urbit/issues/new/choose">submit an issue</a>.</p>
</div>;
export default NotFound;

View File

@ -0,0 +1,40 @@
import React, { Component } from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
type ErrorProps = RouteComponentProps & {
code?: number | string,
description?: string,
error?: Error
};
class ErrorComponent extends Component<ErrorProps> {
render () {
const { code, error, history, description } = this.props;
return (
<div className="pa4 inter tc flex flex-column items-center justify-center w-100 h-100">
<h1 className="mb4 fw2 f2" style={{
fontFeatureSettings: '\'zero\' 1',
}}>
{code ? code : 'Error'}
</h1>
{description ? <p className="tc mb4">{description}</p> : null}
{error ? (
<div className="mb4">
<p className="mb4"><code>&ldquo;{error.message}&rdquo;</code></p>
<details>
<summary>Stack trace</summary>
<pre className="tl">{error.stack}</pre>
</details>
</div>
) : null}
<p className="tc mb4">If this is unexpected, email <code>support@tlon.io</code> or <a className="bb" href="https://github.com/urbit/urbit/issues/new/choose">submit an issue</a>.</p>
{history.length > 1
? <button className="bg-light-green green2 pa2 pointer" onClick={() => history.go(-1) }>Go back</button>
: <button className="bg-light-green green2 pa2 pointer" onClick={() => history.push('/') }>Go home</button>
}
</div>
);
}
}
export default withRouter(ErrorComponent);

View File

@ -0,0 +1,35 @@
import React, { Component } from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import ErrorComponent from './Error';
class ErrorBoundary extends Component<
RouteComponentProps,
{ error?: Error }
> {
constructor(props) {
super(props);
this.state = { error: undefined };
const { history } = this.props;
history.listen((location, action) => {
if (this.state.error) {
this.setState({
error: undefined,
});
}
});
}
componentDidCatch(error) {
this.setState({ error });
return false;
}
render() {
if (this.state.error) {
return (<ErrorComponent error={this.state.error} />)
}
return this.props.children;
}
}
export default withRouter(ErrorBoundary);

View File

@ -1,33 +1,26 @@
import React, { Component } from 'react';
import { Invite } from '../types/invite-update';
export class SidebarInvite extends Component {
onAccept() {
this.props.api.invite.accept('/chat', this.props.uid);
}
onDecline() {
this.props.api.invite.decline('/chat', this.props.uid);
}
export class SidebarInvite extends Component<{invite: Invite, onAccept: Function, onDecline: Function}, {}> {
render() {
const { props } = this;
return (
<div className='w-100 bg-transparent pa4 bb b--gray4 b--gray1-d'>
<div className='w-100 bg-white bg-gray0-d pa4 bb b--gray4 b--gray1-d z-5' style={{position: 'sticky', top: 0}}>
<div className='w-100 v-mid'>
<p className="dib f8 mono gray4-d">
{props.invite.path}
{props.invite.text ? props.invite.text : props.invite.path}
</p>
</div>
<a
className="dib pointer pa2 f9 bg-green2 white mt4"
onClick={this.onAccept.bind(this)}
onClick={this.props.onAccept.bind(this)}
>
Accept Invite
</a>
<a
className="dib pointer ml4 pa2 f9 bg-black bg-gray0-d white mt4"
onClick={this.onDecline.bind(this)}
onClick={this.props.onDecline.bind(this)}
>
Decline
</a>
@ -36,3 +29,4 @@ export class SidebarInvite extends Component {
}
}
export default SidebarInvite;

View File

@ -0,0 +1,20 @@
import React, { Component } from 'react';
export class Toggle extends Component {
render() {
const switchClasses = (this.props.boolean)
? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
: 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
return (
<input
type="checkbox"
style={{ WebkitAppearance: 'none', width: 28 }}
className={switchClasses}
onChange={this.props.change}
/>
);
}
}
export default Toggle;

View File

@ -6,6 +6,7 @@ import {
Group,
Tags,
GroupPolicy,
GroupPolicyDiff,
OpenPolicyDiff,
OpenPolicy,
InvitePolicyDiff,
@ -179,6 +180,8 @@ export default class GroupReducer<S extends GroupState> {
this.openChangePolicy(diff.open, policy);
} else if ('invite' in policy && 'invite' in diff) {
this.inviteChangePolicy(diff.invite, policy);
} else if ('replace' in diff) {
state.groups[resourcePath].policy = diff.replace;
} else {
console.log('bad policy diff');
}

View File

@ -0,0 +1,169 @@
import baseStyled, { ThemedStyledInterface } from "styled-components";
const base = {
white: "rgba(255,255,255,1)",
black: "rgba(0,0,0,1)",
red: "rgba(255,65,54,1)",
yellow: "rgba(255,199,0,1)",
green: "rgba(0,159,101,1)",
blue: "rgba(0,142,255,1)",
};
const scales = {
white10: "rgba(255,255,255,0.1)",
white20: "rgba(255,255,255,0.2)",
white30: "rgba(255,255,255,0.3)",
white40: "rgba(255,255,255,0.4)",
white50: "rgba(255,255,255,0.5)",
white60: "rgba(255,255,255,0.6)",
white70: "rgba(255,255,255,0.7)",
white80: "rgba(255,255,255,0.8)",
white90: "rgba(255,255,255,0.9)",
white100: "rgba(255,255,255,1)",
black10: "rgba(0,0,0,0.1)",
black20: "rgba(0,0,0,0.2)",
black30: "rgba(0,0,0,0.3)",
black40: "rgba(0,0,0,0.4)",
black50: "rgba(0,0,0,0.5)",
black60: "rgba(0,0,0,0.6)",
black70: "rgba(0,0,0,0.7)",
black80: "rgba(0,0,0,0.8)",
black90: "rgba(0,0,0,0.9)",
black100: "rgba(0,0,0,1)",
red10: "rgba(255,65,54,0.1)",
red20: "rgba(255,65,54,0.2)",
red30: "rgba(255,65,54,0.3)",
red40: "rgba(255,65,54,0.4)",
red50: "rgba(255,65,54,0.5)",
red60: "rgba(255,65,54,0.6)",
red70: "rgba(255,65,54,0.7)",
red80: "rgba(255,65,54,0.8)",
red90: "rgba(255,65,54,0.9)",
red100: "rgba(255,65,54,1)",
yellow10: "rgba(255,199,0,0.1)",
yellow20: "rgba(255,199,0,0.2)",
yellow30: "rgba(255,199,0,0.3)",
yellow40: "rgba(255,199,0,0.4)",
yellow50: "rgba(255,199,0,0.5)",
yellow60: "rgba(255,199,0,0.6)",
yellow70: "rgba(255,199,0,0.7)",
yellow80: "rgba(255,199,0,0.8)",
yellow90: "rgba(255,199,0,0.9)",
yellow100: "rgba(255,199,0,1)",
green10: "rgba(0,159,101,0.1)",
green20: "rgba(0,159,101,0.2)",
green30: "rgba(0,159,101,0.3)",
green40: "rgba(0,159,101,0.4)",
green50: "rgba(0,159,101,0.5)",
green60: "rgba(0,159,101,0.6)",
green70: "rgba(0,159,101,0.7)",
green80: "rgba(0,159,101,0.8)",
green90: "rgba(0,159,101,0.9)",
green100: "rgba(0,159,101,1)",
blue10: "rgba(0,142,255,0.1)",
blue20: "rgba(0,142,255,0.2)",
blue30: "rgba(0,142,255,0.3)",
blue40: "rgba(0,142,255,0.4)",
blue50: "rgba(0,142,255,0.5)",
blue60: "rgba(0,142,255,0.6)",
blue70: "rgba(0,142,255,0.7)",
blue80: "rgba(0,142,255,0.8)",
blue90: "rgba(0,142,255,0.9)",
blue100: "rgba(0,142,255,1)",
};
const theme = {
colors: {
white: base.white,
black: base.black,
gray: scales.black60,
lightGray: scales.black30,
washedGray: scales.black10,
red: base.red,
lightRed: scales.red30,
washedRed: scales.red10,
yellow: base.yellow,
lightYellow: scales.yellow30,
washedYellow: scales.yellow10,
green: base.green,
lightGreen: scales.green30,
washedGreen: scales.green10,
blue: base.blue,
lightBlue: scales.blue30,
washedBlue: scales.blue10,
none: "rgba(0,0,0,0)",
scales: scales,
},
fonts: {
sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`,
mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`,
},
// font-size
fontSizes: [
12, // 0
16, // 1
24, // 2
32, // 3
48, // 4
64, // 5
],
// font-weight
fontWeights: {
thin: 300,
regular: 400,
bold: 600,
},
// line-height
lineHeights: {
min: 1.2,
short: 1.333333,
regular: 1.5,
tall: 1.666666,
},
// border, border-top, border-right, border-bottom, border-left
borders: ["none", "1px solid"],
// margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap
space: [
0, // 0
4, // 1
8, // 2
16, // 3
24, // 4
32, // 5
48, // 6
64, // 7
96, // 8
],
// border-radius
radii: [
0, // 0
2, // 1
4, // 2
8, // 3
16, // 4
],
// width, height, min-width, max-width, min-height, max-height
sizes: [
0, // 0
4, // 1
8, // 2
16, // 3
24, // 4
32, // 5
48, // 6
64, // 7
96, // 8
],
// z-index
zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
breakpoints: ["768px", "1024px", "1440px", "2200px"],
};
export type Theme = typeof theme;
export const styled = baseStyled as ThemedStyledInterface<Theme>;
export default theme;

View File

@ -0,0 +1,184 @@
import baseStyled, { ThemedStyledInterface } from "styled-components";
const base = {
white: "rgba(255,255,255,1)",
black: "rgba(0,0,0,1)",
red: "rgba(255,65,54,1)",
yellow: "rgba(255,199,0,1)",
green: "rgba(0,159,101,1)",
blue: "rgba(0,142,255,1)",
};
const scales = {
white05: "rgba(255,255,255,0.05)",
white10: "rgba(255,255,255,0.1)",
white20: "rgba(255,255,255,0.2)",
white30: "rgba(255,255,255,0.3)",
white40: "rgba(255,255,255,0.4)",
white50: "rgba(255,255,255,0.5)",
white60: "rgba(255,255,255,0.6)",
white70: "rgba(255,255,255,0.7)",
white80: "rgba(255,255,255,0.8)",
white90: "rgba(255,255,255,0.9)",
white100: "rgba(255,255,255,1)",
black05: "rgba(0,0,0,0.05)",
black10: "rgba(0,0,0,0.1)",
black20: "rgba(0,0,0,0.2)",
black30: "rgba(0,0,0,0.3)",
black40: "rgba(0,0,0,0.4)",
black50: "rgba(0,0,0,0.5)",
black60: "rgba(0,0,0,0.6)",
black70: "rgba(0,0,0,0.7)",
black80: "rgba(0,0,0,0.8)",
black90: "rgba(0,0,0,0.9)",
black100: "rgba(0,0,0,1)",
red05: "rgba(255,65,54,0.05)",
red10: "rgba(255,65,54,0.1)",
red20: "rgba(255,65,54,0.2)",
red30: "rgba(255,65,54,0.3)",
red40: "rgba(255,65,54,0.4)",
red50: "rgba(255,65,54,0.5)",
red60: "rgba(255,65,54,0.6)",
red70: "rgba(255,65,54,0.7)",
red80: "rgba(255,65,54,0.8)",
red90: "rgba(255,65,54,0.9)",
red100: "rgba(255,65,54,1)",
yellow05: "rgba(255,199,0,0.05)",
yellow10: "rgba(255,199,0,0.1)",
yellow20: "rgba(255,199,0,0.2)",
yellow30: "rgba(255,199,0,0.3)",
yellow40: "rgba(255,199,0,0.4)",
yellow50: "rgba(255,199,0,0.5)",
yellow60: "rgba(255,199,0,0.6)",
yellow70: "rgba(255,199,0,0.7)",
yellow80: "rgba(255,199,0,0.8)",
yellow90: "rgba(255,199,0,0.9)",
yellow100: "rgba(255,199,0,1)",
green05: "rgba(0,159,101,0.05)",
green10: "rgba(0,159,101,0.1)",
green20: "rgba(0,159,101,0.2)",
green30: "rgba(0,159,101,0.3)",
green40: "rgba(0,159,101,0.4)",
green50: "rgba(0,159,101,0.5)",
green60: "rgba(0,159,101,0.6)",
green70: "rgba(0,159,101,0.7)",
green80: "rgba(0,159,101,0.8)",
green90: "rgba(0,159,101,0.9)",
green100: "rgba(0,159,101,1)",
blue05: "rgba(0,142,255,0.05)",
blue10: "rgba(0,142,255,0.1)",
blue20: "rgba(0,142,255,0.2)",
blue30: "rgba(0,142,255,0.3)",
blue40: "rgba(0,142,255,0.4)",
blue50: "rgba(0,142,255,0.5)",
blue60: "rgba(0,142,255,0.6)",
blue70: "rgba(0,142,255,0.7)",
blue80: "rgba(0,142,255,0.8)",
blue90: "rgba(0,142,255,0.9)",
blue100: "rgba(0,142,255,1)",
};
const util = {
cyan: "#00FFFF",
magenta: "#FF00FF",
yellow: "#FFFF00",
black: "#000000",
gray0: "#333333"
};
const theme = {
colors: {
white: util.gray0,
black: base.white,
gray: scales.white60,
lightGray: scales.white30,
washedGray: scales.white05,
red: base.red,
lightRed: scales.red30,
washedRed: scales.red05,
yellow: base.yellow,
lightYellow: scales.yellow30,
washedYellow: scales.yellow10,
green: base.green,
lightGreen: scales.green30,
washedGreen: scales.green10,
blue: base.blue,
lightBlue: scales.blue30,
washedBlue: scales.blue10,
none: "rgba(0,0,0,0)",
scales: scales,
util: util,
},
fonts: {
sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`,
mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`,
},
// font-size
fontSizes: [
12, // 0
16, // 1
24, // 2
32, // 3
48, // 4
64, // 5
],
// font-weight
fontWeights: {
thin: 300,
regular: 400,
bold: 600,
},
// line-height
lineHeights: {
min: 1.2,
short: 1.333333,
regular: 1.5,
tall: 1.666666,
},
// border, border-top, border-right, border-bottom, border-left
borders: ["none", "1px solid"],
// margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap
space: [
0, // 0
4, // 1
8, // 2
16, // 3
24, // 4
32, // 5
48, // 6
64, // 7
96, // 8
],
// border-radius
radii: [
0, // 0
2, // 1
4, // 2
8, // 3
],
// width, height, min-width, max-width, min-height, max-height
sizes: [
0, // 0
4, // 1
8, // 2
16, // 3
24, // 4
32, // 5
48, // 6
64, // 7
96, // 8
],
// z-index
zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
breakpoints: ["768px", "1024px", "1440px", "2200px"],
};
export type Theme = typeof theme;
export const styled = baseStyled as ThemedStyledInterface<Theme>;
export default theme;