Skip to content
This repository was archived by the owner on Apr 14, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/features/gameboard/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export enum EGrip {
none,
}

export type Credentials = {
login: string
api_key: string
}

export type PornList = string[]

export type GameEvent<Args extends any[] = []> = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,13 @@
flex-wrap: wrap;
width: 100%;
margin-top: 10px;
}
}

.PornSetting__error {
color: #cc0000;
}

.PornSetting__textarea {
width: 100%;
min-height: 6.25em;
}
168 changes: 161 additions & 7 deletions src/features/settings/SettingsControls/PornSetting/PornSetting.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React from 'react'
import '../settings.css'
import './PornSetting.css'
import { PornList } from '../../../gameboard/types'
import { E621Posts } from '../../types'
import axios, { AxiosResponse } from 'axios'
import { Credentials, PornList } from '../../../gameboard/types'
import { E621Post, E621User } from '../../types'
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { PornThumbnail } from './PornThumbnail'
import { debounce } from 'lodash'
import reactGA from '../../../../analytics'
import { Blacklist } from '../../../../helpers/blacklist'

interface IPornSettingProps {
credentials: Credentials | null
setCredentials: (newCredentials: Credentials | null) => void
pornList: PornList
setPornList: (newPornList: PornList) => void
}
Expand All @@ -20,6 +23,10 @@ interface IPornSettingState {
flags: {
highRes: boolean
}
credentials: Credentials
addCredentials: boolean
credentialsError: string | null
blacklistTagsString: string | null
}

export class PornSetting extends React.Component<IPornSettingProps, IPornSettingState> {
Expand All @@ -33,18 +40,84 @@ export class PornSetting extends React.Component<IPornSettingProps, IPornSetting
flags: {
highRes: false,
},
credentials: { login: '', api_key: '' },
addCredentials: false,
credentialsError: null,
blacklistTagsString: null,
}

this.updateTags = this.updateTags.bind(this)
this.updateLogin = this.updateLogin.bind(this)
this.updateApiKey = this.updateApiKey.bind(this)
this.saveCredentials = this.saveCredentials.bind(this)
this.clearCredentials = this.clearCredentials.bind(this)
this.loadBlacklist = this.loadBlacklist.bind(this)
this.downloadFromTags = this.downloadFromTags.bind(this)
this.clear = this.clear.bind(this)
}

componentDidUpdate(prevProps: IPornSettingProps) {
if (this.props.credentials !== null && prevProps.credentials === null) {
this.setState({ credentials: this.props.credentials })
this.loadBlacklist()
}
}

updateTags(event: React.ChangeEvent<HTMLInputElement>) {
this.setState({
tags: event.target.value,
})
}

updateLogin(event: React.ChangeEvent<HTMLInputElement>) {
const login = event.target.value
this.setState(prevState => ({
credentials: {
...prevState.credentials!,
login,
},
credentialsError: null,
}))
}

updateApiKey(event: React.ChangeEvent<HTMLInputElement>) {
const api_key = event.target.value
this.setState(prevState => ({
credentials: {
...prevState.credentials!,
api_key,
},
credentialsError: null,
}))
}

saveCredentials() {
// Check to see if these credentials are valid
const config: AxiosRequestConfig = {
params: this.state.credentials,
responseType: 'json',
}
axios
.get(`https://e621.net/users/${this.state.credentials.login}.json`, config)
.then(() => this.props.setCredentials(this.state.credentials))
.catch(() => this.setState({ credentialsError: 'Invalid credentials' }))
}

clearCredentials() {
this.props.setCredentials(null)
this.setState({ addCredentials: false })
}

loadBlacklist() {
const config: AxiosRequestConfig = {
params: this.props.credentials,
responseType: 'json',
}
axios.get(`https://e621.net/users/${this.props.credentials!.login}.json`, config).then((response: AxiosResponse<E621User>) => {
this.setState({ blacklistTagsString: response.data.blacklisted_tags })
})
}

downloadFromTags() {
debounce(() => {
if (localStorage.getItem('allowCookies') !== 'true' || localStorage.getItem('allowCookies') !== null) return
Expand All @@ -54,15 +127,21 @@ export class PornSetting extends React.Component<IPornSettingProps, IPornSetting
label: this.state.tags,
})
}, 2000)()

const config: AxiosRequestConfig = { responseType: 'json' }
if (this.props.credentials != null) {
config.params = this.props.credentials
}

const blacklist = new Blacklist(this.state.blacklistTagsString || '')
const tags = encodeURIComponent(this.state.tags + (this.state.minScore !== null ? ` score:>=${this.state.minScore}` : ''))
axios
.get(`https://e621.net/posts.json?tags=${tags}&limit=${this.state.count}&callback=callback`, {
responseType: 'json',
})
.then((response: AxiosResponse<{ posts: E621Posts }>) => {
.get(`https://e621.net/posts.json?tags=${tags}&limit=${this.state.count}&callback=callback`, config)
.then((response: AxiosResponse<{ posts: E621Post[] }>) => {
this.props.setPornList(
(response.data.posts
.filter(post => /(jpg|png|bmp|jpeg|webp|gif)$/g.test(post.file.ext))
.filter(blacklist.shouldKeepPost)
.map(post => (this.state.flags.highRes ? post.file.url : post.sample.url))
.filter(url => url !== null) as string[])
.filter(url => this.props.pornList.indexOf(url) === -1)
Expand Down Expand Up @@ -92,6 +171,81 @@ export class PornSetting extends React.Component<IPornSettingProps, IPornSetting
<button onClick={this.downloadFromTags}>Import from e621</button>
</div>

<div className="settings-innerrow">
{this.props.credentials ? (
<>
<label>
<span>Use user credentials</span>
<input type="checkbox" checked onChange={this.clearCredentials} />
</label>
<br />
<em>
Logged in.
<br />
You can now use votedup:me, private sets, &amp; your blacklist.
</em>
</>
) : (
<>
<label>
<span>Use user credentials</span>
<input
type="checkbox"
checked={this.state.addCredentials}
onChange={e => this.setState({ addCredentials: e.target.checked })}
/>
</label>
<em>Login to use votedup:me, private sets, &amp; your blacklist.</em>
{this.state.addCredentials ? (
<>
<label>
<span>Username</span>
<input type="text" value={this.state.credentials.login} onChange={this.updateLogin} />
</label>
<br />
<br />
<label>
<span>Api Key</span>
<input type="text" value={this.state.credentials.api_key} onChange={this.updateApiKey} />
</label>
<em>
(found in <a href="https://e621.net/users/home">your account</a> under "Manage API Access")
</em>
<button onClick={this.saveCredentials}>Save credentials</button>
{this.state.credentialsError != null ? <span className="PornSetting__error">{this.state.credentialsError}</span> : null}
</>
) : null}
</>
)}
</div>
<div className="settings-innerrow">
<label>
<span>Use blacklist</span>
<input
type="checkbox"
checked={this.state.blacklistTagsString !== null}
onChange={e => this.setState({ blacklistTagsString: !e.target.checked ? null : '' })}
/>
</label>
{this.state.blacklistTagsString !== null ? (
<>
<br />
<br />
<label>
<span>Blacklisted tags</span>
<textarea
className="PornSetting__textarea"
value={this.state.blacklistTagsString}
onChange={e => this.setState({ blacklistTagsString: e.target.value })}></textarea>
<em>
Put any tag combinations you don't want to see. Each combination should go on a separate line. &nbsp;
<a href="https://e621.net/help/blacklist">View help</a>.
</em>
{this.props.credentials ? <button onClick={this.loadBlacklist}>Reload user blacklist</button> : null}
</label>
</>
) : null}
</div>
<div className="settings-innerrow">
<label>
<span>Score filtering</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function SaveSetting(props: ISaveSettingProps) {
<strong>Share these codes around to let others try your settings.</strong>
<div className="settings-innerrow">
<em>Either copy and paste a code below and click "load", or set everything else up and hit "save" to update the code shown.</em>
<button onClick={() => setCurrentSave(makeSave(props.settings))}>Save</button>
<button onClick={() => setCurrentSave(makeSave(props.settings, /*includeCredentials*/ false))}>Save</button>
<button onClick={() => props.setSettings(prepSave(currentSave, setError))}>Load</button>
</div>
<div className="settings-innerrow">
Expand Down
12 changes: 9 additions & 3 deletions src/features/settings/SettingsControls/SettingsControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react'
import './SettingsControls.css'
import { connect } from 'react-redux'
import { IState } from '../../../store'
import { PornList } from '../../gameboard/types'
import { Credentials, PornList } from '../../gameboard/types'
import { PropsForConnectedComponent } from '../types'
import { PaceSetting } from './PaceSetting/PaceSetting'
import { PornSetting } from './PornSetting/PornSetting'
Expand All @@ -22,6 +22,7 @@ import { PlayerSetting } from './PlayerSetting/PlayerSetting'
interface ISettingsControlsProps extends PropsForConnectedComponent {
pace: IState['settings']['pace']
steepness: IState['settings']['steepness']
credentials: Credentials
pornList: PornList
eventList: IState['settings']['eventList']
duration: IState['settings']['duration']
Expand All @@ -37,6 +38,7 @@ export const SettingsControls = connect(
({
pace: state.settings.pace,
steepness: state.settings.steepness,
credentials: state.settings.credentials,
pornList: state.settings.pornList,
eventList: state.settings.eventList,
duration: state.settings.duration,
Expand All @@ -49,7 +51,7 @@ export const SettingsControls = connect(
)(function (props: ISettingsControlsProps) {
const saveToLocalStorage = useRef(
debounce((props: ISettingsControlsProps) => {
localStorage.setItem('lastSession', makeSave(props))
localStorage.setItem('lastSession', makeSave(props, /*includeCredentials*/ true))
}, 1000),
)
useEffect(() => saveToLocalStorage.current(props))
Expand All @@ -72,7 +74,11 @@ export const SettingsControls = connect(

<EventsSetting eventList={props.eventList} setEventList={(newList) => props.dispatch(SettingsActions.SetEventList(newList))} />

<PornSetting pornList={props.pornList} setPornList={(newList) => props.dispatch(SettingsActions.SetPornList(newList))} />
<PornSetting
credentials={props.credentials}
setCredentials={(newCredentials) => props.dispatch(SettingsActions.SetCredentials(newCredentials))}
pornList={props.pornList}
setPornList={(newList) => props.dispatch(SettingsActions.SetPornList(newList))} />

<WalltakerSetting
enabled={Boolean(props.walltakerLink ?? false)}
Expand Down
8 changes: 7 additions & 1 deletion src/features/settings/store/actions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { PornList, EventToken, HypnoMode, PlayerParts, PlayerGender } from '../../gameboard/types'
import { PornList, EventToken, HypnoMode, PlayerParts, PlayerGender, Credentials } from '../../gameboard/types'

export const T_OPEN_DIALOG = 'OPEN_DIALOG'
export const T_CLOSE_DIALOG = 'CLOSE_DIALOG'
export const T_SET_MIN_PACE = 'SET_MIN_PACE'
export const T_SET_MAX_PACE = 'SET_MAX_PACE'
export const T_SET_STEEPNESS = 'SET_STEEPNESS'
export const T_SET_DURATION = 'SET_DURATION'
export const T_SET_CREDENTIALS = 'SET_CREDENTIALS'
export const T_SET_PORN_LIST = 'SET_PORN_LIST'
export const T_SET_EVENT_LIST = 'SET_EVENT_LIST'
export const T_SET_HYPNO_MODE = 'SET_HYPNO_MODE'
Expand Down Expand Up @@ -44,6 +45,11 @@ class SettingsActionsBase {
payload: duration,
})

SetCredentials = (credentials: Credentials | null) => ({
type: T_SET_CREDENTIALS as typeof T_SET_CREDENTIALS,
payload: credentials,
})

SetPornList = (pornList: PornList) => ({
type: T_SET_PORN_LIST as typeof T_SET_PORN_LIST,
payload: pornList,
Expand Down
10 changes: 9 additions & 1 deletion src/features/settings/store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
T_SET_MAX_PACE,
T_OPEN_DIALOG,
T_CLOSE_DIALOG,
T_SET_CREDENTIALS,
T_SET_PORN_LIST,
T_SET_DURATION,
T_SET_EVENT_LIST,
Expand All @@ -15,7 +16,7 @@ import {
T_SET_PLAYER_GENDER,
T_SET_PLAYER_PARTS,
} from './actions'
import { PornList, EventToken, HypnoMode, PlayerParts, PlayerGender } from '../../gameboard/types'
import { PornList, EventToken, HypnoMode, PlayerParts, PlayerGender, Credentials } from '../../gameboard/types'
import { events } from '../../gameboard/events/index'

export interface ISettingsState {
Expand All @@ -26,6 +27,7 @@ export interface ISettingsState {
}
steepness: number
duration: number
credentials: Credentials | null
pornList: PornList
eventList: EventToken['id'][]
hypnoMode: HypnoMode
Expand All @@ -48,6 +50,7 @@ export const SettingsDefaultState: ISettingsState = {
},
steepness: 0.05,
duration: 6000,
credentials: null,
pornList: [],
eventList: events.map(event => event.id),
hypnoMode: HypnoMode.JOI,
Expand Down Expand Up @@ -97,6 +100,11 @@ export function SettingsReducer(state: ISettingsState = SettingsDefaultState, ac
...state,
duration: action.payload,
}
case T_SET_CREDENTIALS:
return {
...state,
credentials: action.payload,
}
case T_SET_PORN_LIST:
return {
...state,
Expand Down
Loading