simplify sign calculation
This commit is contained in:
parent
ecc3d02167
commit
cf95b3a345
|
|
@ -23,6 +23,7 @@ class UserController extends Controller
|
|||
{
|
||||
/** @var User */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user
|
||||
->makeHidden(['password', 'ip', 'remember_token', 'verification_token']);
|
||||
}
|
||||
|
|
@ -31,11 +32,11 @@ class UserController extends Controller
|
|||
{
|
||||
$user = Auth::user();
|
||||
|
||||
[$from, $to] = explode(',', option('sign_score'));
|
||||
[$min, $max] = explode(',', option('sign_score'));
|
||||
$scoreIntro = trans('user.score-intro.introduction', [
|
||||
'initial_score' => option('user_initial_score'),
|
||||
'score-from' => $from,
|
||||
'score-to' => $to,
|
||||
'score-from' => $min,
|
||||
'score-to' => $max,
|
||||
'return-score' => option('return_score')
|
||||
? trans('user.score-intro.will-return-score')
|
||||
: trans('user.score-intro.no-return-score'),
|
||||
|
|
@ -60,10 +61,6 @@ class UserController extends Controller
|
|||
$parsedown = new Parsedown();
|
||||
|
||||
return view('user.index')->with([
|
||||
'statistics' => [
|
||||
'players' => $this->calculatePercentageUsed($user->players->count(), option('score_per_player')),
|
||||
'storage' => $this->calculatePercentageUsed($this->getStorageUsed($user), option('score_per_storage')),
|
||||
],
|
||||
'score_intro' => $scoreIntro,
|
||||
'rates' => [
|
||||
'storage' => option('score_per_storage'),
|
||||
|
|
@ -80,89 +77,55 @@ class UserController extends Controller
|
|||
{
|
||||
$user = Auth::user();
|
||||
|
||||
return json('', 0, [
|
||||
return response()->json([
|
||||
'user' => [
|
||||
'score' => $user->score,
|
||||
'lastSignAt' => $user->last_sign_at,
|
||||
],
|
||||
'stats' => [
|
||||
'players' => $this->calculatePercentageUsed($user->players->count(), option('score_per_player')),
|
||||
'storage' => $this->calculatePercentageUsed($this->getStorageUsed($user), option('score_per_storage')),
|
||||
'rate' => [
|
||||
'storage' => (int) option('score_per_storage'),
|
||||
'players' => (int) option('score_per_player'),
|
||||
],
|
||||
'usage' => [
|
||||
'players' => $user->players->count(),
|
||||
'storage' => (int) Texture::where('uploader', $user->uid)->sum('size'),
|
||||
],
|
||||
'signAfterZero' => (bool) option('sign_after_zero'),
|
||||
'signGapTime' => (int) option('sign_gap_time'),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function calculatePercentageUsed(int $used, int $rate): array
|
||||
{
|
||||
$user = Auth::user();
|
||||
// Initialize default value to avoid division by zero.
|
||||
$result['used'] = $used;
|
||||
$result['total'] = 'UNLIMITED';
|
||||
$result['percentage'] = 0;
|
||||
|
||||
if ($rate != 0) {
|
||||
$result['total'] = $used + floor($user->score / $rate);
|
||||
$result['percentage'] = $result['total'] ? $used / $result['total'] * 100 : 100;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getStorageUsed(User $user)
|
||||
{
|
||||
return Texture::where('uploader', $user->uid)->select('size')->sum('size');
|
||||
}
|
||||
|
||||
public function sign()
|
||||
{
|
||||
/** @var User */
|
||||
$user = Auth::user();
|
||||
if ($this->getSignRemainingTime($user) <= 0) {
|
||||
$acquiredScore = rand(...explode(',', option('sign_score')));
|
||||
|
||||
$lastSignTime = Carbon::parse($user->last_sign_at);
|
||||
$remainingTime = option('sign_after_zero')
|
||||
? Carbon::now()->diffInSeconds(
|
||||
$lastSignTime <= Carbon::today() ? $lastSignTime : Carbon::tomorrow(),
|
||||
false
|
||||
)
|
||||
: Carbon::now()->diffInSeconds(
|
||||
$lastSignTime->addHours((int) option('sign_gap_time')),
|
||||
false
|
||||
);
|
||||
|
||||
if ($remainingTime <= 0) {
|
||||
[$min, $max] = explode(',', option('sign_score'));
|
||||
$acquiredScore = rand((int) $min, (int) $max);
|
||||
$user->score += $acquiredScore;
|
||||
$user->last_sign_at = Carbon::now();
|
||||
$user->save();
|
||||
$gap = option('sign_gap_time');
|
||||
|
||||
return json(trans('user.sign-success', ['score' => $acquiredScore]), 0, [
|
||||
'score' => $user->score,
|
||||
'storage' => $this->calculatePercentageUsed($this->getStorageUsed($user), option('score_per_storage')),
|
||||
'remaining_time' => $gap > 1 ? round($gap) : $gap,
|
||||
]);
|
||||
} else {
|
||||
$remaining_time = $this->getUserSignRemainingTimeWithPrecision($user);
|
||||
|
||||
return json(trans('user.cant-sign-until', [
|
||||
'time' => $remaining_time >= 1
|
||||
? $remaining_time : round($remaining_time * 60),
|
||||
'unit' => $remaining_time >= 1
|
||||
? trans('user.time-unit-hour') : trans('user.time-unit-min'),
|
||||
]), 1);
|
||||
return json('', 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getUserSignRemainingTimeWithPrecision(User $user)
|
||||
{
|
||||
$hours = $this->getSignRemainingTime($user) / 3600;
|
||||
|
||||
return $hours > 1 ? round($hours) : $hours;
|
||||
}
|
||||
|
||||
protected function getSignRemainingTime(User $user)
|
||||
{
|
||||
$lastSignTime = Carbon::parse($user->last_sign_at);
|
||||
|
||||
if (option('sign_after_zero')) {
|
||||
return Carbon::now()->diffInSeconds(
|
||||
$lastSignTime <= Carbon::today() ? $lastSignTime : Carbon::tomorrow(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return Carbon::now()->diffInSeconds($lastSignTime->addHours(option('sign_gap_time')), false);
|
||||
}
|
||||
|
||||
public function sendVerificationEmail()
|
||||
{
|
||||
if (!option('require_verification')) {
|
||||
|
|
@ -187,7 +150,6 @@ class UserController extends Controller
|
|||
try {
|
||||
Mail::to($user->email)->send(new EmailVerification(url($url)));
|
||||
} catch (\Exception $e) {
|
||||
// Write the exception to log
|
||||
report($e);
|
||||
|
||||
return json(trans('user.verification.failed', ['msg' => $e->getMessage()]), 2);
|
||||
|
|
@ -231,6 +193,7 @@ class UserController extends Controller
|
|||
public function handleProfile(Request $request, Filter $filter, Dispatcher $dispatcher)
|
||||
{
|
||||
$action = $request->input('action', '');
|
||||
/** @var User */
|
||||
$user = Auth::user();
|
||||
$addition = $request->except('action');
|
||||
|
||||
|
|
@ -333,6 +296,7 @@ class UserController extends Controller
|
|||
{
|
||||
$request->validate(['tid' => 'required|integer']);
|
||||
$tid = $request->input('tid');
|
||||
/** @var User */
|
||||
$user = auth()->user();
|
||||
|
||||
$can = $filter->apply('user_can_update_avatar', true, [$user, $tid]);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ interface Props {
|
|||
icon: string
|
||||
color: string
|
||||
used: number
|
||||
total: number
|
||||
unused: number
|
||||
unit: string
|
||||
}
|
||||
|
||||
const InfoBox: React.FC<Props> = (props) => {
|
||||
const percentage = (props.used / props.total) * 100
|
||||
const total = ~~(props.used + props.unused)
|
||||
const percentage = (props.used / total) * 100
|
||||
|
||||
return (
|
||||
<div className={`info-box bg-${props.color}`}>
|
||||
|
|
@ -20,7 +21,7 @@ const InfoBox: React.FC<Props> = (props) => {
|
|||
<div className="info-box-content">
|
||||
<span className="info-box-text">{props.name}</span>
|
||||
<span className="info-box-number">
|
||||
<b>{props.used}</b> / {props.total} {props.unit}
|
||||
<b>{props.used}</b> / {total} {props.unit}
|
||||
</span>
|
||||
<div className="progress">
|
||||
<div className="progress-bar" style={{ width: `${percentage}%` }} />
|
||||
|
|
|
|||
|
|
@ -9,22 +9,18 @@ import urls from '@/scripts/urls'
|
|||
import * as breakpoints from '@/styles/breakpoints'
|
||||
import InfoBox from './InfoBox'
|
||||
import SignButton from './SignButton'
|
||||
import * as scoreUtils from './scoreUtils'
|
||||
|
||||
type ScoreInfo = {
|
||||
signAfterZero: boolean
|
||||
signGapTime: number
|
||||
stats: { players: Stat; storage: Stat }
|
||||
rate: { players: number; storage: number }
|
||||
usage: { players: number; storage: number }
|
||||
user: { score: number; lastSignAt: string }
|
||||
}
|
||||
|
||||
type Stat = {
|
||||
used: number
|
||||
total: number
|
||||
}
|
||||
|
||||
type SignReturn = {
|
||||
score: number
|
||||
storage: Stat
|
||||
}
|
||||
|
||||
const ScoreTitle = styled.p`
|
||||
|
|
@ -47,9 +43,12 @@ const ScoreNotice = styled.p`
|
|||
|
||||
const Dashboard: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [players, setPlayers] = useState<Stat>({ used: 0, total: 1 })
|
||||
const [storage, setStorage] = useState<Stat>({ used: 0, total: 1 })
|
||||
const [score, setScore] = useTween(0)
|
||||
const [players, setPlayers] = useState(0)
|
||||
const [storage, setStorage] = useState(0)
|
||||
const [score, setScore] = useState(0)
|
||||
const [tweenedScore, setTweenedScore] = useTween(0)
|
||||
const [playersRate, setPlayersRate] = useState(1)
|
||||
const [storageRate, setStorageRate] = useState(1)
|
||||
const [lastSign, setLastSign] = useState(new Date())
|
||||
const [canSignAfterZero, setCanSignAfterZero] = useState(false)
|
||||
const [signGap, setSignGap] = useState(24)
|
||||
|
|
@ -57,12 +56,13 @@ const Dashboard: React.FC = () => {
|
|||
useEffect(() => {
|
||||
const fetchInfo = async () => {
|
||||
setLoading(true)
|
||||
const { data } = await fetch.get<fetch.ResponseBody<ScoreInfo>>(
|
||||
urls.user.score(),
|
||||
)
|
||||
setPlayers(data.stats.players)
|
||||
setStorage(data.stats.storage)
|
||||
const data = await fetch.get<ScoreInfo>(urls.user.score())
|
||||
setPlayers(data.usage.players)
|
||||
setStorage(data.usage.storage)
|
||||
setTweenedScore(data.user.score)
|
||||
setScore(data.user.score)
|
||||
setPlayersRate(data.rate.players)
|
||||
setStorageRate(data.rate.storage)
|
||||
setLastSign(new Date(data.user.lastSignAt))
|
||||
setCanSignAfterZero(data.signAfterZero)
|
||||
setSignGap(data.signGapTime)
|
||||
|
|
@ -80,10 +80,15 @@ const Dashboard: React.FC = () => {
|
|||
if (code === 0) {
|
||||
toast.success(message)
|
||||
setLastSign(new Date())
|
||||
setTweenedScore(data.score)
|
||||
setScore(data.score)
|
||||
setStorage(data.storage)
|
||||
} else {
|
||||
toast.warning(message)
|
||||
const remainingTime = scoreUtils.remainingTime(
|
||||
lastSign,
|
||||
signGap,
|
||||
canSignAfterZero,
|
||||
)
|
||||
toast.warning(scoreUtils.remainingTimeText(remainingTime))
|
||||
}
|
||||
setLoading(false)
|
||||
}, [])
|
||||
|
|
@ -101,17 +106,17 @@ const Dashboard: React.FC = () => {
|
|||
color="teal"
|
||||
icon="gamepad"
|
||||
name={t('user.used.players')}
|
||||
used={players.used}
|
||||
total={players.total}
|
||||
used={players}
|
||||
unused={score / playersRate}
|
||||
unit=""
|
||||
/>
|
||||
{storage.used > 1024 ? (
|
||||
{storage > 1024 ? (
|
||||
<InfoBox
|
||||
color="maroon"
|
||||
icon="hdd"
|
||||
name={t('user.used.storage')}
|
||||
used={~~(storage.used / 1024)}
|
||||
total={~~(storage.total / 1024)}
|
||||
used={~~(storage / 1024)}
|
||||
unused={~~(score / storageRate / 1024)}
|
||||
unit="MB"
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -119,8 +124,8 @@ const Dashboard: React.FC = () => {
|
|||
color="maroon"
|
||||
icon="hdd"
|
||||
name={t('user.used.storage')}
|
||||
used={storage.used}
|
||||
total={storage.total}
|
||||
used={storage}
|
||||
unused={score / storageRate}
|
||||
unit="KB"
|
||||
/>
|
||||
)}
|
||||
|
|
@ -128,7 +133,7 @@ const Dashboard: React.FC = () => {
|
|||
<div className="col-md-4 text-center">
|
||||
<ScoreTitle>{t('user.cur-score')}</ScoreTitle>
|
||||
<Score data-toggle="modal" data-target="#modal-score-instruction">
|
||||
{~~score}
|
||||
{~~tweenedScore}
|
||||
</Score>
|
||||
<ScoreNotice>{t('user.score-notice')}</ScoreNotice>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ export function remainingTime(
|
|||
export function remainingTimeText(remainingTime: number): string {
|
||||
const time = remainingTime / ONE_MINUTE
|
||||
return time < 60
|
||||
? t('user.sign-remain-time', {
|
||||
? t('user.signRemainingTime', {
|
||||
time: ~~time,
|
||||
unit: t('user.time-unit-min'),
|
||||
})
|
||||
: t('user.sign-remain-time', {
|
||||
: t('user.signRemainingTime', {
|
||||
time: ~~(time / 60),
|
||||
unit: t('user.time-unit-hour'),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,41 +4,42 @@ import * as fetch from '@/scripts/net'
|
|||
import { t } from '@/scripts/i18n'
|
||||
import urls from '@/scripts/urls'
|
||||
import Dashboard from '@/views/user/Dashboard'
|
||||
import * as scoreUtils from '@/views/user/Dashboard/scoreUtils'
|
||||
|
||||
jest.mock('@/scripts/net')
|
||||
|
||||
function scoreInfo(data = {}, user = {}, stats = {}) {
|
||||
function scoreInfo(data = {}, user = {}, usage = {}) {
|
||||
return {
|
||||
data: {
|
||||
user: { score: 600, lastSignAt: '2018-08-07 16:06:49', ...user },
|
||||
stats: {
|
||||
players: { used: 3, total: 15 },
|
||||
storage: { used: 5, total: 20 },
|
||||
...stats,
|
||||
},
|
||||
signAfterZero: false,
|
||||
signGapTime: '24',
|
||||
...data,
|
||||
user: { score: 600, lastSignAt: '2018-08-07 16:06:49', ...user },
|
||||
usage: {
|
||||
players: 3,
|
||||
storage: 5,
|
||||
...usage,
|
||||
},
|
||||
rate: {
|
||||
players: 10,
|
||||
storage: 1,
|
||||
},
|
||||
signAfterZero: false,
|
||||
signGapTime: '24',
|
||||
...data,
|
||||
}
|
||||
}
|
||||
|
||||
describe('info box', () => {
|
||||
it('players', async () => {
|
||||
fetch.get.mockResolvedValue(
|
||||
scoreInfo({}, { score: 0 }, { players: { used: 13, total: 21 } }),
|
||||
)
|
||||
fetch.get.mockResolvedValue(scoreInfo({}, { score: 40 }))
|
||||
|
||||
const { getByText } = render(<Dashboard />)
|
||||
await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
|
||||
expect(getByText('13')).toBeInTheDocument()
|
||||
expect(getByText(/21/)).toBeInTheDocument()
|
||||
expect(getByText('3')).toBeInTheDocument()
|
||||
expect(getByText(/7/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
describe('storage', () => {
|
||||
it('in KB', async () => {
|
||||
fetch.get.mockResolvedValue(
|
||||
scoreInfo({}, { score: 0 }, { storage: { used: 700, total: 800 } }),
|
||||
scoreInfo({}, { score: 100 }, { storage: 700 }),
|
||||
)
|
||||
|
||||
const { getByText } = render(<Dashboard />)
|
||||
|
|
@ -50,13 +51,13 @@ describe('info box', () => {
|
|||
|
||||
it('in MB', async () => {
|
||||
fetch.get.mockResolvedValue(
|
||||
scoreInfo({}, { score: 0 }, { storage: { used: 7168, total: 10240 } }),
|
||||
scoreInfo({}, { score: 3072 }, { storage: 4096 }),
|
||||
)
|
||||
|
||||
const { getByText } = render(<Dashboard />)
|
||||
await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
|
||||
expect(getByText('7')).toBeInTheDocument()
|
||||
expect(getByText(/10/)).toBeInTheDocument()
|
||||
expect(getByText('4')).toBeInTheDocument()
|
||||
expect(getByText(/7/)).toBeInTheDocument()
|
||||
expect(getByText(/MB/)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
@ -67,11 +68,11 @@ describe('sign', () => {
|
|||
fetch.get.mockResolvedValue(scoreInfo())
|
||||
})
|
||||
|
||||
it('should succeed', async () => {
|
||||
it('succeeded', async () => {
|
||||
fetch.post.mockResolvedValue({
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: { score: 900, storage: { used: 5, total: 25 } },
|
||||
data: { score: 900 },
|
||||
})
|
||||
|
||||
const { getByRole, getByText, queryByText } = render(<Dashboard />)
|
||||
|
|
@ -83,18 +84,22 @@ describe('sign', () => {
|
|||
expect(getByText('ok')).toBeInTheDocument()
|
||||
expect(getByRole('status')).toHaveClass('alert-success')
|
||||
expect(button).toBeDisabled()
|
||||
expect(queryByText(/25/)).toBeInTheDocument()
|
||||
expect(queryByText(/905/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should fail', async () => {
|
||||
fetch.post.mockResolvedValue({ code: 1, message: 'f', data: {} })
|
||||
it('failed', async () => {
|
||||
fetch.post.mockResolvedValue({ code: 1, message: '' })
|
||||
|
||||
const { getByRole, getByText } = render(<Dashboard />)
|
||||
await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
|
||||
|
||||
fireEvent.click(getByRole('button'))
|
||||
await waitFor(() => expect(fetch.post).toBeCalledWith(urls.user.sign()))
|
||||
expect(getByText('f')).toBeInTheDocument()
|
||||
|
||||
const remainingTime = 24 * 3600 * 1000
|
||||
expect(
|
||||
getByText(scoreUtils.remainingTimeText(remainingTime)),
|
||||
).toBeInTheDocument()
|
||||
expect(getByRole('alert')).toHaveClass('alert-warning')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@ cur-score: Current Score
|
|||
score-notice: Click the score to show introduction.
|
||||
sign: Sign
|
||||
sign-success: Signed successfully. You got :score scores.
|
||||
time-unit-hour: h
|
||||
time-unit-min: min
|
||||
cant-sign-until: You can't sign in in :time :unit
|
||||
last-sign: Last signed at :time
|
||||
sign-remain-time: Available after :time :unit
|
||||
announcement: Announcement
|
||||
no-unread: No new notifications.
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,10 @@ class UserControllerTest extends TestCase
|
|||
$uid = $user->uid;
|
||||
factory(\App\Models\Player::class)->create(['uid' => $uid]);
|
||||
|
||||
$announcement = (new Parsedown())->text(option_localized('announcement'));
|
||||
$this->actingAs($user)
|
||||
->get('/user')
|
||||
->assertViewHas('statistics')
|
||||
->assertSee((new Parsedown())->text(option_localized('announcement')), false)
|
||||
->assertSee((string) $user->score);
|
||||
->assertSee($announcement, false);
|
||||
$filter->assertApplied('grid:user.index');
|
||||
$filter->assertApplied('user_avatar', function ($url, $user) use ($uid) {
|
||||
$this->assertTrue(Str::contains($url, '/avatar/user/'.$uid));
|
||||
|
|
@ -60,26 +59,22 @@ class UserControllerTest extends TestCase
|
|||
|
||||
$this->actingAs($user)
|
||||
->get('/user/score-info')
|
||||
->assertJson(['data' => [
|
||||
->assertJson([
|
||||
'user' => [
|
||||
'score' => $user->score,
|
||||
'lastSignAt' => $user->last_sign_at,
|
||||
],
|
||||
'stats' => [
|
||||
'players' => [
|
||||
'used' => 1,
|
||||
'total' => 11,
|
||||
'percentage' => 1 / 11 * 100,
|
||||
],
|
||||
'storage' => [
|
||||
'used' => 0,
|
||||
'total' => $user->score,
|
||||
'percentage' => 0,
|
||||
],
|
||||
'rate' => [
|
||||
'storage' => (int) option('score_per_storage'),
|
||||
'players' => (int) option('score_per_player'),
|
||||
],
|
||||
'usage' => [
|
||||
'players' => 1,
|
||||
'storage' => 0,
|
||||
],
|
||||
'signAfterZero' => option('sign_after_zero'),
|
||||
'signGapTime' => option('sign_gap_time'),
|
||||
]]);
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSign()
|
||||
|
|
@ -87,7 +82,7 @@ class UserControllerTest extends TestCase
|
|||
option(['sign_score' => '50,50']);
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
// Success
|
||||
// success
|
||||
$this->actingAs($user)
|
||||
->postJson('/user/sign')
|
||||
->assertJson([
|
||||
|
|
@ -95,57 +90,25 @@ class UserControllerTest extends TestCase
|
|||
'message' => trans('user.sign-success', ['score' => 50]),
|
||||
'data' => [
|
||||
'score' => option('user_initial_score') + 50,
|
||||
'storage' => [
|
||||
'percentage' => 0,
|
||||
'total' => option('user_initial_score') + 50,
|
||||
'used' => 0,
|
||||
],
|
||||
'remaining_time' => (int) option('sign_gap_time'),
|
||||
],
|
||||
]);
|
||||
|
||||
// Remaining time is greater than 0
|
||||
// remaining time is greater than 0
|
||||
$user = factory(User::class)->create(['last_sign_at' => Carbon::now()]);
|
||||
option(['sign_gap_time' => 2]);
|
||||
$this->actingAs($user)
|
||||
->postJson('/user/sign')
|
||||
->assertJson([
|
||||
'code' => 1,
|
||||
'message' => trans(
|
||||
'user.cant-sign-until',
|
||||
[
|
||||
'time' => 2,
|
||||
'unit' => trans('user.time-unit-hour'),
|
||||
]
|
||||
),
|
||||
]);
|
||||
->assertJson(['code' => 1]);
|
||||
|
||||
// Can sign after 0 o'clock
|
||||
// can sign after 0 o'clock
|
||||
option(['sign_after_zero' => true]);
|
||||
$user = factory(User::class)->create(['last_sign_at' => Carbon::now()]);
|
||||
$diff = \Carbon\Carbon::now()->diffInSeconds(\Carbon\Carbon::tomorrow());
|
||||
if ($diff / 3600 >= 1) {
|
||||
$diff = round($diff / 3600);
|
||||
$unit = 'hour';
|
||||
} else {
|
||||
$diff = round($diff / 60);
|
||||
$unit = 'min';
|
||||
}
|
||||
$this->actingAs($user)
|
||||
->postJson('/user/sign')
|
||||
->assertJson([
|
||||
'code' => 1,
|
||||
'message' => trans(
|
||||
'user.cant-sign-until',
|
||||
[
|
||||
'time' => $diff,
|
||||
'unit' => trans("user.time-unit-$unit"),
|
||||
]
|
||||
),
|
||||
]);
|
||||
->assertJson(['code' => 1]);
|
||||
|
||||
$user = factory(User::class)->create([
|
||||
'last_sign_at' => \Carbon\Carbon::today(),
|
||||
'last_sign_at' => Carbon::today(),
|
||||
]);
|
||||
$this->actingAs($user)->postJson('/user/sign')->assertJson(['code' => 0]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user