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:
Ian McKenzie 2024-08-28 20:34:22 -07:00 committed by GitHub
parent 68738bd227
commit 96fdd94a01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 184 additions and 1 deletions

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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.

View file

@ -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)
} }

View file

@ -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

View file

@ -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>

View file

@ -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();

View file

@ -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",