mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Create a section in the history panel to reset scene activity (#5168)
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
parent
68738bd227
commit
96fdd94a01
9 changed files with 184 additions and 1 deletions
|
|
@ -276,6 +276,13 @@ type Mutation {
|
||||||
"Sets the resume time point (if provided) and adds the provided duration to the scene's play duration"
|
"Sets the resume time point (if provided) and adds the provided duration to the scene's play duration"
|
||||||
sceneSaveActivity(id: ID!, resume_time: Float, playDuration: Float): Boolean!
|
sceneSaveActivity(id: ID!, resume_time: Float, playDuration: Float): Boolean!
|
||||||
|
|
||||||
|
"Resets the resume time point and play duration"
|
||||||
|
sceneResetActivity(
|
||||||
|
id: ID!
|
||||||
|
reset_resume: Boolean
|
||||||
|
reset_duration: Boolean
|
||||||
|
): Boolean!
|
||||||
|
|
||||||
"Increments the play count for the scene. Returns the new play count value."
|
"Increments the play count for the scene. Returns the new play count value."
|
||||||
sceneIncrementPlayCount(id: ID!): Int!
|
sceneIncrementPlayCount(id: ID!): Int!
|
||||||
@deprecated(reason: "Use sceneAddPlay instead")
|
@deprecated(reason: "Use sceneAddPlay instead")
|
||||||
|
|
|
||||||
|
|
@ -847,6 +847,24 @@ func (r *mutationResolver) SceneSaveActivity(ctx context.Context, id string, res
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) SceneResetActivity(ctx context.Context, id string, resetResume *bool, resetDuration *bool) (ret bool, err error) {
|
||||||
|
sceneID, err := strconv.Atoi(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("converting id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.withTxn(ctx, func(ctx context.Context) error {
|
||||||
|
qb := r.repository.Scene
|
||||||
|
|
||||||
|
ret, err = qb.ResetActivity(ctx, sceneID, utils.IsTrue(resetResume), utils.IsTrue(resetDuration))
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
func (r *mutationResolver) SceneIncrementPlayCount(ctx context.Context, id string) (ret int, err error) {
|
func (r *mutationResolver) SceneIncrementPlayCount(ctx context.Context, id string) (ret int, err error) {
|
||||||
sceneID, err := strconv.Atoi(id)
|
sceneID, err := strconv.Atoi(id)
|
||||||
|
|
|
||||||
|
|
@ -1267,6 +1267,27 @@ func (_m *SceneReaderWriter) QueryCount(ctx context.Context, sceneFilter *models
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetActivity provides a mock function with given fields: ctx, sceneID, resetResume, resetDuration
|
||||||
|
func (_m *SceneReaderWriter) ResetActivity(ctx context.Context, sceneID int, resetResume bool, resetDuration bool) (bool, error) {
|
||||||
|
ret := _m.Called(ctx, sceneID, resetResume, resetDuration)
|
||||||
|
|
||||||
|
var r0 bool
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int, bool, bool) bool); ok {
|
||||||
|
r0 = rf(ctx, sceneID, resetResume, resetDuration)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int, bool, bool) error); ok {
|
||||||
|
r1 = rf(ctx, sceneID, resetResume, resetDuration)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// ResetO provides a mock function with given fields: ctx, id
|
// ResetO provides a mock function with given fields: ctx, id
|
||||||
func (_m *SceneReaderWriter) ResetO(ctx context.Context, id int) (int, error) {
|
func (_m *SceneReaderWriter) ResetO(ctx context.Context, id int) (int, error) {
|
||||||
ret := _m.Called(ctx, id)
|
ret := _m.Called(ctx, id)
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ type SceneWriter interface {
|
||||||
OHistoryWriter
|
OHistoryWriter
|
||||||
ViewHistoryWriter
|
ViewHistoryWriter
|
||||||
SaveActivity(ctx context.Context, sceneID int, resumeTime *float64, playDuration *float64) (bool, error)
|
SaveActivity(ctx context.Context, sceneID int, resumeTime *float64, playDuration *float64) (bool, error)
|
||||||
|
ResetActivity(ctx context.Context, sceneID int, resetResume bool, resetDuration bool) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SceneReaderWriter provides all scene methods.
|
// SceneReaderWriter provides all scene methods.
|
||||||
|
|
|
||||||
|
|
@ -1234,6 +1234,30 @@ func (qb *SceneStore) SaveActivity(ctx context.Context, id int, resumeTime *floa
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qb *SceneStore) ResetActivity(ctx context.Context, id int, resetResume bool, resetDuration bool) (bool, error) {
|
||||||
|
if err := qb.tableMgr.checkIDExists(ctx, id); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record := goqu.Record{}
|
||||||
|
|
||||||
|
if resetResume {
|
||||||
|
record["resume_time"] = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if resetDuration {
|
||||||
|
record["play_duration"] = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(record) > 0 {
|
||||||
|
if err := qb.tableMgr.updateByID(ctx, id, record); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (qb *SceneStore) GetURLs(ctx context.Context, sceneID int) ([]string, error) {
|
func (qb *SceneStore) GetURLs(ctx context.Context, sceneID int) ([]string, error) {
|
||||||
return scenesURLsTableMgr.get(ctx, sceneID)
|
return scenesURLsTableMgr.get(ctx, sceneID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,18 @@ mutation SceneSaveActivity(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutation SceneResetActivity(
|
||||||
|
$id: ID!
|
||||||
|
$reset_resume: Boolean!
|
||||||
|
$reset_duration: Boolean!
|
||||||
|
) {
|
||||||
|
sceneResetActivity(
|
||||||
|
id: $id
|
||||||
|
reset_resume: $reset_resume
|
||||||
|
reset_duration: $reset_duration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
mutation SceneAddPlay($id: ID!, $times: [Timestamp!]) {
|
mutation SceneAddPlay($id: ID!, $times: [Timestamp!]) {
|
||||||
sceneAddPlay(id: $id, times: $times) {
|
sceneAddPlay(id: $id, times: $times) {
|
||||||
count
|
count
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,10 @@ import {
|
||||||
useSceneIncrementPlayCount,
|
useSceneIncrementPlayCount,
|
||||||
useSceneResetO,
|
useSceneResetO,
|
||||||
useSceneResetPlayCount,
|
useSceneResetPlayCount,
|
||||||
|
useSceneResetActivity,
|
||||||
} from "src/core/StashService";
|
} from "src/core/StashService";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
|
import { useToast } from "src/hooks/Toast";
|
||||||
import { TextField } from "src/utils/field";
|
import { TextField } from "src/utils/field";
|
||||||
import TextUtils from "src/utils/text";
|
import TextUtils from "src/utils/text";
|
||||||
|
|
||||||
|
|
@ -72,9 +74,19 @@ const History: React.FC<{
|
||||||
|
|
||||||
const HistoryMenu: React.FC<{
|
const HistoryMenu: React.FC<{
|
||||||
hasHistory: boolean;
|
hasHistory: boolean;
|
||||||
|
showResetResumeDuration: boolean;
|
||||||
onAddDate: () => void;
|
onAddDate: () => void;
|
||||||
onClearDates: () => void;
|
onClearDates: () => void;
|
||||||
}> = ({ hasHistory, onAddDate, onClearDates }) => {
|
resetResume: () => void;
|
||||||
|
resetDuration: () => void;
|
||||||
|
}> = ({
|
||||||
|
hasHistory,
|
||||||
|
showResetResumeDuration,
|
||||||
|
onAddDate,
|
||||||
|
onClearDates,
|
||||||
|
resetResume,
|
||||||
|
resetDuration,
|
||||||
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -101,6 +113,22 @@ const HistoryMenu: React.FC<{
|
||||||
<FormattedMessage id="actions.clear_date_data" />
|
<FormattedMessage id="actions.clear_date_data" />
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
)}
|
)}
|
||||||
|
{showResetResumeDuration && (
|
||||||
|
<Dropdown.Item
|
||||||
|
className="bg-secondary text-white"
|
||||||
|
onClick={() => resetResume()}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="actions.reset_resume_time" />
|
||||||
|
</Dropdown.Item>
|
||||||
|
)}
|
||||||
|
{showResetResumeDuration && (
|
||||||
|
<Dropdown.Item
|
||||||
|
className="bg-secondary text-white"
|
||||||
|
onClick={() => resetDuration()}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="actions.reset_play_duration" />
|
||||||
|
</Dropdown.Item>
|
||||||
|
)}
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
|
|
@ -142,6 +170,7 @@ interface ISceneHistoryProps {
|
||||||
|
|
||||||
export const SceneHistoryPanel: React.FC<ISceneHistoryProps> = ({ scene }) => {
|
export const SceneHistoryPanel: React.FC<ISceneHistoryProps> = ({ scene }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const Toast = useToast();
|
||||||
|
|
||||||
const [dialogs, setDialogs] = React.useState({
|
const [dialogs, setDialogs] = React.useState({
|
||||||
playHistory: false,
|
playHistory: false,
|
||||||
|
|
@ -160,6 +189,8 @@ export const SceneHistoryPanel: React.FC<ISceneHistoryProps> = ({ scene }) => {
|
||||||
const [incrementOCount] = useSceneIncrementO(scene.id);
|
const [incrementOCount] = useSceneIncrementO(scene.id);
|
||||||
const [decrementOCount] = useSceneDecrementO(scene.id);
|
const [decrementOCount] = useSceneDecrementO(scene.id);
|
||||||
const [resetO] = useSceneResetO(scene.id);
|
const [resetO] = useSceneResetO(scene.id);
|
||||||
|
const [resetResume] = useSceneResetActivity(scene.id, true, false);
|
||||||
|
const [resetDuration] = useSceneResetActivity(scene.id, false, true);
|
||||||
|
|
||||||
function dateStringToISOString(time: string) {
|
function dateStringToISOString(time: string) {
|
||||||
const date = TextUtils.stringToFuzzyDateTime(time);
|
const date = TextUtils.stringToFuzzyDateTime(time);
|
||||||
|
|
@ -221,6 +252,52 @@ export const SceneHistoryPanel: React.FC<ISceneHistoryProps> = ({ scene }) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleResetResume() {
|
||||||
|
try {
|
||||||
|
await resetResume({
|
||||||
|
variables: {
|
||||||
|
id: scene.id,
|
||||||
|
reset_resume: true,
|
||||||
|
reset_duration: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Toast.success(
|
||||||
|
intl.formatMessage(
|
||||||
|
{ id: "toast.updated_entity" },
|
||||||
|
{
|
||||||
|
entity: intl.formatMessage({ id: "scene" }).toLocaleLowerCase(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Toast.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleResetDuration() {
|
||||||
|
try {
|
||||||
|
await resetDuration({
|
||||||
|
variables: {
|
||||||
|
id: scene.id,
|
||||||
|
reset_resume: false,
|
||||||
|
reset_duration: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Toast.success(
|
||||||
|
intl.formatMessage(
|
||||||
|
{ id: "toast.updated_entity" },
|
||||||
|
{
|
||||||
|
entity: intl.formatMessage({ id: "scene" }).toLocaleLowerCase(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Toast.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function maybeRenderDialogs() {
|
function maybeRenderDialogs() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -296,8 +373,11 @@ export const SceneHistoryPanel: React.FC<ISceneHistoryProps> = ({ scene }) => {
|
||||||
</Button>
|
</Button>
|
||||||
<HistoryMenu
|
<HistoryMenu
|
||||||
hasHistory={playHistory.length > 0}
|
hasHistory={playHistory.length > 0}
|
||||||
|
showResetResumeDuration={true}
|
||||||
onAddDate={() => setDialogPartial({ addPlay: true })}
|
onAddDate={() => setDialogPartial({ addPlay: true })}
|
||||||
onClearDates={() => setDialogPartial({ playHistory: true })}
|
onClearDates={() => setDialogPartial({ playHistory: true })}
|
||||||
|
resetResume={() => handleResetResume()}
|
||||||
|
resetDuration={() => handleResetDuration()}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</h5>
|
</h5>
|
||||||
|
|
@ -336,8 +416,11 @@ export const SceneHistoryPanel: React.FC<ISceneHistoryProps> = ({ scene }) => {
|
||||||
</Button>
|
</Button>
|
||||||
<HistoryMenu
|
<HistoryMenu
|
||||||
hasHistory={oHistory.length > 0}
|
hasHistory={oHistory.length > 0}
|
||||||
|
showResetResumeDuration={false}
|
||||||
onAddDate={() => setDialogPartial({ addO: true })}
|
onAddDate={() => setDialogPartial({ addO: true })}
|
||||||
onClearDates={() => setDialogPartial({ oHistory: true })}
|
onClearDates={() => setDialogPartial({ oHistory: true })}
|
||||||
|
resetResume={() => handleResetResume()}
|
||||||
|
resetDuration={() => handleResetDuration()}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</h5>
|
</h5>
|
||||||
|
|
|
||||||
|
|
@ -786,6 +786,21 @@ export const useSceneResetO = (id: string) =>
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const useSceneResetActivity = (
|
||||||
|
id: string,
|
||||||
|
reset_resume: boolean,
|
||||||
|
reset_duration: boolean
|
||||||
|
) =>
|
||||||
|
GQL.useSceneResetActivityMutation({
|
||||||
|
variables: { id, reset_resume, reset_duration },
|
||||||
|
update(cache, result) {
|
||||||
|
if (!result.data?.sceneResetActivity) return;
|
||||||
|
|
||||||
|
evictTypeFields(cache, sceneMutationImpactedTypeFields);
|
||||||
|
evictQueries(cache, sceneMutationImpactedQueries);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const useSceneGenerateScreenshot = () =>
|
export const useSceneGenerateScreenshot = () =>
|
||||||
GQL.useSceneGenerateScreenshotMutation();
|
GQL.useSceneGenerateScreenshotMutation();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,8 @@
|
||||||
"remove_from_gallery": "Remove from Gallery",
|
"remove_from_gallery": "Remove from Gallery",
|
||||||
"rename_gen_files": "Rename generated files",
|
"rename_gen_files": "Rename generated files",
|
||||||
"rescan": "Rescan",
|
"rescan": "Rescan",
|
||||||
|
"reset_play_duration": "Reset play duration",
|
||||||
|
"reset_resume_time": "Reset resume time",
|
||||||
"reset_cover": "Restore Default Cover",
|
"reset_cover": "Restore Default Cover",
|
||||||
"reshuffle": "Reshuffle",
|
"reshuffle": "Reshuffle",
|
||||||
"running": "running",
|
"running": "running",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue